Joshua's Docs - PHP - Assorted Notes
Light

Pulling In and Referencing Other Files

Include and Require Method Calls

WARNING: Avoid using relative paths whenever possible. A common approach (example) is to define constants / globals that hold absolute paths that can be shared across your environment, so you don't always have to do a lookup with dirname(__FILE__) all the time.

NOTE: For both include and require, parenthesis around file name are not required, nor recommended.

Include vs Require

Short answer: require will throw a fatal error (halt execution) if it can't load the file, whereas include will not.

Echoing out JavaScript, CSS, and other Text

If you have a local file, such as a JavaScript .js file, that you simply want to echo out into the page, you can use echo file_get_contents() or the readfile() function.

For example:

<?php echo file_get_contents($jsDirPath . '/myJsFile.js'); ?>

Echoing Out or Returning HTML

One of the things that is both really fun about PHP, as well as strange, is the way it interfaces directly with HTML and sending content to the browser "over the wire". There are many different ways to return HTML content from PHP, starting with simply stopping and starting the interpretation of PHP with PHP tags.

<?php
$test = 123;
// Stopping PHP...
?>

<div> Hello from HTML! </div>

<?php
// Resuming PHP
?>

You can also use the short echo tag to pause and resume PHP:
<?= 'this will echo' ?>

However, this is not the only way to echo out HTML, and more importantly, it doesn't allow us to capture the resulting HTML as a string instead of echoing out. For that, we need to look to other solutions.

A handy solution in PHP for this is output buffering. This lets you send all output from the current script into a buffer, then capture the value of that buffer as a string and do whatever you want with it (return it from a function, pass it around, etc.).

Buffering Output Example
<?php

function generateButtonsHtml(int $count) {
	$done = 1;
	ob_start();
	while ($done < $count + 1) {
		?>
		<button id="button-<?= $done ?>"> Button #<?= $done ?></button>
		<?php
		$done++;
	}
	$htmlStr = ob_get_clean();
	return $htmlStr;
}

$count = isset($_GET['count']) ? intval($_GET['count']) : 1;
echo generateButtonsHtml($count);

For general alternative approaches to building up / wrapping strings, you should check out this StackOverflow answer.

Echoing Out or Returning JSON

There are two main steps to returning JSON.

  1. If you are building an API, or something that is returning the JSON as the only response within the network request, then you should set the content-type header:
    • header('Content-type: application/json');
  2. For actually echoing out the JSON, there are multiple options
    • If you have a PHP object, and want to return it as JSON, use echo json_encode($myPhpObj)
    • If you have the JSON as a raw string, you can simply echo it
    • If you want to paste JSON into a PHP file to echo back out later, you can use the Heredoc / Nowdoc syntax to avoid having to escape line breaks or special characters

Strings

Instantiating and Escaping Complex or Multi-Line Strings

If you have a really long string literal that spans multiple lines, and/or contains a lot of characters that would need to be escaped, a handy tool to use is the Heredoc syntax (or Nowdoc).

The safest usage, if you don't need variable replacement, is the Nowdoc syntax:

$myStr = <<<'EOD'
Hello World
On the second line
Final line!
EOD;

📄 SO: Quick comparison between the two types

Heredoc is also a close replacement for JavaScript's backticks / template literal strings.

How do I...

  • Initialize an array:
    • Simple (numerical indexed): $myArr = array("alpha", "bravo");
    • Associative:
      $myArr = array(
      	"primaryColor" => "red",
      	"secondaryColor" => "blue"
      );
      
      // Or, with v > 5.4
      $myArr = [
      	"primaryColor" => "red",
      	"secondaryColor" => "blue"
      ];
  • Add to an array
    • array_push($myArr, $valToPush);
  • Get the length of something
  • Use types
  • Use default values in function arguments
    • See Docs
    • General rules:
      • Default value must be constant value, not variable or function call
      • Default args / values should always come last
  • Safely check variables without throwing undefined errors?
    • empty() or isset() seem to be the only two ways to safely check for defined.
    • Things like gettype() or equality checks will emit errors if the variable is undefined
      • E.g.: You cannot do if ($_POST['myPostKey']) {} as it will throw a Undefined index error
  • Suppress errors
    • It almost goes without saying that errors shouldn't be ignored; best option is try / catch, log properly, etc.
    • However, if you really do need to suppress, PHP has a call syntax - prefix command with @, which is an error control operator
  • Get a PHP REPL
    • PHP CLI, with -a flag for interactive shell (php -a) (docs)

Objects

The PHP manual has a good guide on objects in PHP.

Instantiating Objects in PHP

There are multiple options for how to instantiate an "object" in PHP.

  • Option: Create a class with public members, and call with new
    class MyClass {
    	public $primaryColor = 'red';
    }
    $myObj = new MyClass();
  • Option: Cast an associative array to an object:
    $myObj = (object) array(
    	'primaryColor' => 'red'
    );

For creating an empty object (for example, as a bucket for future values), you can use the methods above, but in a streamlined way:

  • You can use $emptyObj = new stdClass();
  • You can use $emptyObj = (object) [];

Traits

https://www.php.net/manual/en/language.oop5.traits.php

Kinda similar to TS interfaces. Allows you to reuse methods declared in trait, in a class, without extending (bypass PHP's "no multiple inheritance" rule)

Important notes:

  • Traits are not just declarations; they (can) include the whole implementation
  • A function declared in a class using a trait, with the same name as a function in the trait, will override that function
    • HOWEVER, if two traits try to override the same method: a fatal error is produced, if the conflict is not explicitly resolved
      • To resolve, make sure you use MyTraitA::myMethod instead of B
  • Within trait functions you can use parent::
  • Syntax is use MyTrait as first line below class opening brace, to use trait

VSCode

PHP, like some other languages out there, has a user-base that has been kind of slow to adopt VSCode as an IDE. As a bit of a self-fulfilling prophecy (why build something if no one is going to use it?), PHP support in VSCode is a little lackluster (at least at the moment).

In general, the PHP community tends to use software from the JetBrains company; particularly PhpStorm. To be fair to PHP devs, PhpStorm has been around since 2009, while VSCode has only been around since 2015.

WordPress Editing in VSCode

I recommend either:

  • Edit your theme / plugin by opening an entire Wordpress installation directory (i.e., the root folder that contains wp-config.php), so VSCode can pick up the function declarations
    • And / Or...
  • Install the WordPress Snippets extension

Formatting PHP in VSCode

One of the most popular extensions for PHP in VSCode is PHP Intelephense by Ben Mewburn. This can function as a formatter, but it has limited controls when it comes to that (for example, disabling Allman braces is not yet an option).

For strong coding standards enforcement, you probably want to be using PHP CodeSniffer (aka phpcs). It can be used as a standalone CLI tool, but there is also a VSCode extension for linting, as well as one that handles both linting and auto-formatting, called PHP Sniffer & Beautifier (aka PHPSAB)

Another alternative is phpfmt - PHP formatter. Although it is no longer maintained (😢), it is one of the few PHP formatting extensions that does not require PHPCS as a dependency. If you want to default to K&R style braces instead of Allman, you can use these settings.

If you run into issues using PHPSAB in a multi-root setup, you might want to try hard-coding any setting that takes a path, with absolute paths, not relative paths. For example, the phpsab.executablePathCS and phpsab.executablePathCBF settings.

Linting and Formatting

PHP CodeSniffer

Github page: squizlabs/PHP_CodeSniffer

PHPCS - Issues

One issue I ran into PHPCS, or at least with the auto-fixer + PHPCS, was around the spacing and alignment of comments. I kept getting Found precision alignment of 1 spaces. mixed with Expected 2 space(s) before asterisk; 1 found as the linting error, but the auto-fixer would also seem to get stuck and not touch the file.

Comment Spacing Issue

This issue of the auto-fixer getting stuck seems to be caused by when you start with this input:

 /**
 * My Comment
 */

then run the fixer, it turns into:

 /**
  * My Comment
  */

Which it has no clue how to fix and does not attempt to. However, if you MANUALLY make sure FIRST line has no leading space, it will pass and even fix if lower lines are off. For example, input:

/**
  * My Comment
  */

Auto-fixed, passes lint:

/**
 * My Comment
 */

Debugging

XDebug

XDebug is the best solution. Usually all it takes to enable is to download the matching DLL (use this tool to find matching version) and place in extension folder. Then add these lines to php.ini config file (example):

zend_extension=xdebug-2.7.2-7.2-vc15-x86_64
xdebug.remote_enable=1
xdebug.remote_autostart=1

In VSCode, you will need an extension to enable PHP debugging - I recommend the PHP Debug Adapter by Becker. this is usually the config I use in launch.json for debugging:

{
	"name": "Listen for XDebug",
	"type": "php",
	"request": "launch",
	"port": 9003
}

Warning: Adding XDebug will usually add some additional load time in your local environment

I believe past versions of XDebug used port 9000 - default is now 9003

Manually triggering XDebug

Aside from adding breakpoints, you can also trigger your IDE debugger manually (similar to JavaScript debugger; kw), by using xdebug_break() (details). Just make sure to not have in production code!!!

Profiling with XDebug

Follow the official guide.

Sample php.ini settings:

xdebug.profiler_enable=1
xdebug.mode=profile
xdebug.output_dir="C:/Users/Joshua/AppData/Local/Temp/xdebug_prof"

For profile viewing, WinCacheGrind was easy to use on Windows.

VSCode PHP Debugging - Ignoring Files

If you are using lots of PHP tooling, you might run into the issue that your local dev tooling (unrelated to the runtime of what you are actually building) is triggering the VSCode debugger extension.

For example, I first encountered this with phpcs / code_sniffer throwing exceptions while linting open files. If I had an open stack-trace and then switched tab, the linter would interrupt the debugging process and inject a new stack trace (very annoying!).

If you are using the popular PHP debug adapter extension, the way around this is to tell the extension to ignore certain file patterns while running a debug session. You can do this with ignore: [] under your specific launch configuration. E.g.:

{
	"name": "Listen for Xdebug",
	"type": "php",
	"request": "launch",
	"port": 9003,
	"ignore": ["**/vendor/**/*.php"]
},

For the full list of launch.json settings, make sure to check out the docs for PHP Debug.

Type Annotations / Comments

In general, support for type annotations through comments generally depends on the IDE you are using and/or installed extensions. However, The general accepted standard is PHPDoc, as opposed to alternatives. Here is the PHPDoc Reference Page.

Here are some extra tricks for using PHPDoc:

  • If you are using intelephense with VSCode, you can check out intelephense-docs
  • Although you cannot document things like stdclass inline, you can add PHP files that are strictly for annotating structures and don't actually do anything at run-time
  • If you are trying to use types within a namespaced file, you will need to prefix the type with / to avoid the doc interpreter from thinking the type belongs to the current namespace. E.g. @param \WP_POST $post instead of @param WP_POST $post

As of right now (09/13/2020), there is no option to ignore single errors / type issues with intelephense. See Issue #568.

Markdown Source Last Updated:
Mon Jul 19 2021 01:40:30 GMT+0000 (Coordinated Universal Time)
Markdown Source Created:
Mon Aug 19 2019 17:06:24 GMT+0000 (Coordinated Universal Time)
© 2021 Joshua Tzucker, Built with Gatsby
Feedback