Maintaining Codes Libraries in PHP

OCLC has several code libraries that we make available in the PHP programming language to make it easier to use our web services. The OCLC/Auth Library is a key library we provide to help developers authenticate and authorize to our web services. It was originally released in 2013. Over the last three years, we've made subtle changes to the library to improve error handling and to support a more robust set of tests.

This fall, I began work on some new functionality for the library to support an upcoming change in our OAuth server, which will allow clients to call a "Where Are You From" (WAYF) screen during the authentication process rather than specifying a particular institution to have the user log in at. While working on these changes and reviewing the library, I discovered that the HTTP client the library uses (Guzzle) was seriously out of date and needed to be updated. The update required upgrading the HTTP client from version 3.x to version 6.x. This kind of substantial version update meant all of the code that utilized the HTTP library needed to be rewritten and tested. Additionally, the update meant the OCLC/Auth library needed to explicitly support PHP 7, which it hadn't been tested against.

First, I had to update the version of PHP I was using in my development environment to PHP 7. Because I use a Bitnami MAMP stack to do development, this simply was a matter of downloading the latest Bitnami MAMP stack, installing it, and then including the PHP from this package in my path. I work on a Mac, so updating my path meant opening my .bash_profile file and making sure I included the following line:

export PATH=/Applications/mampstack-7.0.13-1/php/bin:$PATH

Then, I saved the file and restarted Terminal. Now when I enter “php -version” in the command line, I see that the version of PHP is PHP 7.0.13.

Second, I had to update the library's composer.json file to specify that the newer version of Guzzle should be used.

"require" : {
    "guzzlehttp/guzzle" : ">=6.0"
}

Next, I had install the updated library into my development environment using Composer. For those of you who aren't familiar with Composer, it is a package and dependency management tool for PHP. Composer allows you to specify the code libraries your library or application requires and use it to install and manage those libraries. Composer can also be used to update the code libraries being used when you develop and test your software.

So by running the command “composer update” in the root directory of the code library, composer updates the libraries being used. Unfortunately, when I ran this command, some errors were returned. The errors indicated that my development dependencies were out of date and also needed to be updated. Development dependencies are code libraries required if you are developing or testing the code in question. In my case, I needed to update the version of PHPUnit I was using for the library's unit tests. So I made the following additional changes to my composer.json file.

"require-dev" : {
    "phpunit/phpunit" : ">=3",
}

Then, I ran the command for updating again.

composer update

This installed updates for all the relevant libraries. Once I had the relevant libraries installed, I could begin making changes to the code itself. To do this, I needed to locate all the HTTP requests in the code. Once I'd located the HTTP requests, I had to update those lines of code to use the new syntax required by the updated library. The new lines of code look like this:

$guzzleOptions = array(
    'headers' => array(
        'Authorization' => $authorization,
        'Accept' => 'application/json',
        'User-Agent' => static::$userAgent
    ),
    'allow_redirects' => array(
       'strict' => true
    ),
    'timeout' => 60
);

if (static::$testServer){
    $guzzleOptions['verify'] = false;
}

if (isset($logger)){
    $stack = HandlerStack::create();
    $stack->push(
            Middleware::log(
                    $logger,
                    new MessageFormatter('{req_body} - {res_body}')
                    )
            );
    $guzzleOptions['handler'] = $stack;
}

$client = new Client($guzzleOptions);
try {
    $response = $client->post($url);
    self::parseTokenResponse($response->getBody());
} catch (RequestException $error) {
    $errorCode = (string) $error->getResponse()->getStatusCode();
    $response = $error->getResponse()->getBody(true);
    $responseBody = json_decode($response, true);
    if (isset($responseBody['message'])){
        $errorMessage = $responseBody['message'];
    } elseif (isset($responseBody['error']['errorMessage'])) {
        $errorMessage = $responseBody['error']['errorMessage'];
    } else {
        $errorMessage = $this->response;
    }
    Throw new \Exception($errorCode . ' ' . $errorMessage);
}

After I completed the code changes, I needed to test them using PHPUnit. I did this by running “phpunit” in the command line while in the library project directory. Running this command caused a series of warnings that I was using deprecated code syntax for creating my mocks. A mock is a method or object that simulates the behavior of a real method or object in a scenario. Using mocks for testing can be very advantageous because it removes dependencies. I'm using them so that I can avoid making an actual HTTP request to OCLC's infrastructure to test my library code.

In order to fix the testing errors, I updated my PHPUnit tests to use the new syntax for creating and using mock objects. With all these changes made, I then committed and pushed the new code to GitHub.

Committing and pushing the new code caused my continuous integration (CI) scripts to run. Running the CI scripts failed because I failed to remove the test cases for older versions of PHP. So I made an update to my CI script to remove testing of versions of PHP older than 5.6.

Because of the changes to the HTTP client library, the examples in the README and other documentation needed to be updated to reflect the new syntax for making an HTTP request to a web service. Additionally, whenever a new release is created, the composer.json file needs to be updated to reflect a new release number. With the new release number in the composer file, I could go into GitHub and create a new release of the code library. Once I published the new release, it became available for consumers to download and utilize.

I'm still working on changes to the library to support clients being able to direct users to a WAYF screen. This work is going on in a separate branch of the code, so all the changes I've made to the HTTP client within the code need to be added to this branch. I'll talk a little bit more about creating branches and managing code changes across branches in a post next month.

  • Karen Coombs

    Karen Coombs

    Senior Product Analyst