Category: PHP

View header and body with curl

I recently discovered the -i switch to curl! I have no idea why I didn't know about this before…

Curl is one of those tools that every developer should know. It's universal and tends to be available everywhere.

When developing APIs, I prefer to use curl to view the output of a request like this:

-v is for verbose and so you get told all the information you could possibly want. However, usually, I only want to know the response's headers and body.

Enter the -i switch!

Much better!

-i is for include and from the man page:

Include the HTTP-header in the output. The HTTP-header includes things like server-name, date ofthe document, HTTP-version and more…

This is exactly what I want without the information that I don't!

Filtering the PSR-7 body in middleware

Sometimes, there's a requirement to alter the data in the Response's body after it has been created by your controller action. For example, we may want to ensure that our brand name is consistently capitalised.

One way to do this is to create middleware that looks like this:

This works perfectly, so if my response body contains "I consult through my company, nineteen feet.", then the output of the $brandFilter middleware is "I consult through thought my company, Nineteen Feet.", which is exactly what we want.

That's great, but what happens if the new string is shorter than the old one? For instance, suppose I want to replace all uses of "nineteen feet" with "19FT".

Using the same middleware, but changing the str_ireplace call with:

creates this output:

That's not quite what we wanted! This result is because the new string is shorter, so the characters from the old string are still in the PHP stream and as we haven't change the length, they are still there. The PSR-7 StreamInterface doesn't allow us access to the underlying stream, so we can't call ftruncate on it.

Hence, the easiest solution is to replace the Response's PSR-7 StreamInterface object with a new one containing what we need:

The output is now what we expect:

Slim 3.4.0 now provides PSR-7!

I've been neglecting Slim's PR queue recently, so this weekend I dedicated a lot of time to merging all the good work that our contributors have done. As a result, I'm delighted to release version 3.4.0!

This release has a larger set of changes in it than I would have ideally liked which is a direct consequence of having gone two months between releases rather than one.

One particularly interesting addition that we have a made this release is adding a provide section to our composer.json file:

This means that we have informed Composer that Slim provides a valid implementation of the interfaces in psr/http-message-implementation virtual package that defines the PSR-7 interfaces.

This means that when you install a Composer package that requires psr/http-message-implementation in your Slim project, then Composer will now recognise that Slim satisfies this requirement and won't insist you install another PSR-7 implementation just for package resolution!

There's lots of other goodies in 3.4.0, so check out the release notes and upgrade!

Overriding Slim 3's error handling

Slim 3 registers two error handers:

This means that if you want to override the default error handlers, you need to override both error handlers with your own code.

Each error handler is a callable. The signatures are:

  • errorHandler: function ($request, $response, $exception)
  • phpErrorHandler: function ($request, $response, $error)

To override a error handler, simply register a new callable with the Container:

If you don't want to repeat yourself, you can register the phpErrorHandler like this:

Note that you can register any PHP callable, so a class with an __invoke() method also works.

Handling PHP notices

As an aside, Slim 2's error handler catches PHP notices for you (which can be infuriating or very useful!). Slim 3 doesn't do this by default, so if you want to catch PHP notices then you need to register your own error handler like this:

That's all there is to it.

A few composer tips

I recently learned about a couple of features of composer that I thought I'd write down here so that I don't forget them! I also had to deal with a conflict in composer.lock recently, so I've noted down how I solved that too.

List installed versions

To list the current versions of all installed dependencies:

The output looks something like:

Very useful for working out exactly what's installed.

Set PHP version

To set the version of PHP that composer will use to resolve dependencies, add this to your composer.json file:

You can now run composer update on a PHP 7 installation and it will create a composer.lock file suitable for a server running PHP 5.6.19.

Resolving a conflict in composer.lock

When you merge a feature branch into develop and get a conflict in composer.lock, I've found these strategies work best for me:

Just the hash

If the only conflict is in the "hash" and "content-hash" lines, then pick either choice and then run:

Any other conflict

For any other conflict where you want to keep the current set of versions on develop:

  1. Retrieve the correct lock file for develop: git merge --ours
  2. Add in each new dependency in the merged composer.json that's not in the original develop's composer.json using

The end result is a composer.lock file with the original information from develop along with the new packages from the feature branch.

Determining the image type of a file

One thing I learnt recently which I probably should have known already is that getimagesize() returns more than just the width and height of the image.

I've always used it like this:

However, getimagesize() also returns up to 5 more pieces of information.

Interestingly, the data array is a mix of indexed elements and named elements For example, for a file I uploaded while testing a PR, the output of print_r(getimagesize($filename)) is:

A very strange decision when designing the response of this function!

Index 2 is the file type which is an "IMAGETYPE" constant, such as IMAGETYPE_PNG. Note that there's also a constant called IMG_PNG, but this is a different number, so make sure you use the right one!

This is useful for file uploads. Consider this data in $_FILES:

In this case, there is no extension on the uploaded filename and the type is application/octet-stream. If I want to resize this image using gd, then I'm going to need to know whether to use imagejpeg, imagegif or imagepng. A simple way to do this is something like this:

It's easier than dealing with the type from $_FILES too and by writing it down, maybe I'll remember it.

The internal pointer of an array

I discovered recently that if you walk through an array using array_walk or array_walk_recursive, then the array's internal pointer is left at the end of the array. Clearly this isn't something that I've needed to know before!

This code example shows the fundamentals:

The output is NULL and you use reset() to put the internal pointed back to the start.

Foreach is different in PHP 7!

Note that foreach works the same way in PHP 5, but works differently in PHP 7:

will output string(1) "a" on PHP 7 and NULL on PHP 5.

Using Composer with shared hosting

I can't use Composer because I'm using shared hosting and don't have SSH

I've seen this sentiment a few times now, so this seems like a good time to point out that you do not need SSH access to your server in order to use Composer. In fact, I don't run Composer on a live server (regardless of whether it's using shared hosting) and it's not on my list of things to do in the near future.

What you do need is a process where you handle your Composer dependencies on your own computer where you have PHP running.

In my view, you have two choices: commit your Composer dependencies directly to your project or write a build script that runs Composer for you and uploads the resulting files.

In either case, you need to install Composer globally, so follow the instructions relevant to your operating system.

Let's look at the process for both options, starting with committing your Composer dependencies.

Commit Composer dependencies

The easiest way to handle Composer dependencies is to run Composer locally and commit the vendor directory into your repository.

Write your website, using Composer, as usual and commit composer.json, composer.lock and all the files in vendor.

Note the following:

  1. Ensure that your .gitignore file does not exclude vendor. This is very common when starting from a skeleton project or using a tool like artisan to create your project.
  2. Ensure that you only use packages that have a release number. That is never use dev-master in your composer.json as if you do, Composer will install it via git and you won't be able to add it to your own repository. There are good reasons for avoiding dev-master dependencies anyway.

Your git repository now has all the files needed to run the website directly within it and so you can now simply upload your website to your shared host as you usually do.

Use a build script

If you don't want to commit the dependencies to your git repository, another solution is to write a script that you run locally that downloads the dependencies and thenuploads the files to your host.

The process looks like this:

  1. Check's out your source code to a clean directory
  2. Runs composer install
  3. Removes all .git directories and any another files and directories that shouldn't be on your live site
  4. Uploads all the remaining files to your shared host. (if your host uses FTP, then use ncftpput for this as it supports recursion)
  5. Deletes the directory

Note that in this situation, you need to ensure that the vendor directory is excluded in your .gitignore file and that composer.lock is committed to git.

Run the script every time you need to put new code onto your live site.

Summary

As you can see, using Composer to manage the dependencies of your PHP project has nothing to do with your final choice of hosting for your live site. You should always have the ability to run your PHP website on your local computer and so you can deal with Composer there and it's simply a case of transferring the right files to live.

In general, I'm a big fan of scripting things that are done by hand, so recommend using a build script, even if you choose to commit your dependencies to your own repository.

Running Phan against Slim 3

Having installed Phan, I decided to use it against the upcoming Slim 3 codebase.

Phan needs a list of files to scan, and the place I started was with Lorna's article on Generating a file list for Phan.

I started by removing all the developer dependencies:

and then built the file list for the vendor and Slim directories. I started by using Lorna's grep statement, but that found too many non-PHP files in vendor, so I ended up with:

I then reorganised the list to put interfaces and traits at the top as order matters & then ran Phan:

A lot of issues have been found – most of them due to missing dependent classes & interfaces, so I ignored those when working through the list. Next time, I'll add the relevant vendor files to see if that catches anything else.

The remaining issues found were all type errors related to the docblock saying one thing and the code doing another. These will need tidying up.

It was an interesting exercise and I encourage you to run Phan or a similar tool over your own codebase.

Installing Phan on OS X

I use Homebrew for my local PHP installation on OS X and am currently running PHP 7.0.0 RC8.

Phan is a static analyser for PHP 7 which was written by Rasmus and then rewritten by Andrew Morrison. As it benefits from PHP 7's abstract syntax tree it can find all kinds of subtle errors, so I wanted to install it locally to have a play with it.

I started by trying to install Phan into my global composer install so that it's available on my path. As it's not released on Packagist, I edited ~/.composer/composer.json and added:

to the top, so the I could then install via the standard composer method:

This immediately failed as I don't have the Nikita's ast extension installed:

To install this extension:

Now edit you php.ini file (/usr/local/etc/php/7.0/php.ini for Homebrew installations) and add extension=ast.so at the bottom.

Having installed the ast PHP 7 extension, Phan will now install:

Now, Phan works from my command line!