Posts tagged PHP
PHPUnit – How to Run PHPUnit Against a Specific Test
4The other day I was debugging an error in one of my unit tests, and found it hard to track down because when I ran PHPUnit, it ran all the tests contained in the file where my problem unit test was located. After some Googling and reading the PHPUnit Api Docs, I found that you can specify a test, among other tests, by adding a comment with the @group annotation. Using @group and any group name you wish, you can tell PHPUnit, from the command line, to test only tests belonging to a specific group.
Consider the following:
public function testArrayCount()
{
$this->assertCount(2, array('foo', 'bar');
}
/**
* @group grain
*/
public function testArrayPopulated()
{
$this->assertTrue(empty(array()));
}
Notice how I specified the testArrayPopulated() method with a @group grain annotation, now I can tell PHPUnit to only test tests belonging to the grain group.
./phpunit path/to/your/tests/ --colors --group grain
Now, when PHPUnit runs, it will only run tests against those tests to which you have assigned the grain group. This @group annotation is a really cool feature of PHPUnit and helped me a lot with debugging my unit tests.
PHP – PHPUnit – Use PHPUnit Without PEAR
2Abstract
PHPUnit is a great tool to protect us developers from introducing new defects when adding new features or re-factoring code. However there is one HUGE downside to PHPUnit; it must be installed using PEAR. Personally, I don’t like ‘auto-installers’, I’d prefer to know what exactly is happening behind the scenes with regards to which libraries are required and how they are being called. So when I needed to use PHPUnit on a recent project I shed a tear thinking I would have to break down and install using PEAR.
PHPUnit is not a mythical creature, it doesn’t have magical powers, and as such, it should not intimidate us. It is PHP code, plain and simple, and like any other PHP API Libraries it can be interwoven into our application. So after breaking down the PHPUnit source code, I realized it could be installed without going through PEAR, and without too much headache.
I wrote this article with the goal that it may help others, and maybe even make it’s way to the lead developer of PHPUnit, where he may start using a better autoloading strategy. However, I must warn you to not hold me responsible for any “issues” that may arise on your system by following this article, as it’s strictly demonstrative at this point. There may come a time where I add this code to a GIT repo and officially support it, but not at this point. If you are ok with that, then read on.
Also, note that you can change any of the following guidelines to suit your specific needs. What I am outlining here worked for me, but may not work for you, so feel free to change what you need to, in order to accomplish your end game.
Base Directories
To make things work, we need a base directories from which to work. As with any setting throughout this article, you can change to meet your needs.
Test directory
This directory will be where we put our actual unit tests:
/home/mpurcell/projects/core/test
Vendor directory
This directory will be the location where we download and extract PHPUnit source code:
/home/mpurcell/projects/vendor
Source Code:
Now that we have our directories in place, lets get PHPUnit source code. Normally you would use PEAR to download, extract, and prepare the source code, but personally I don’t like things of this nature being done for me, I am a big boy, I can handle it myself. So I download each of the core packages manually, then extract them, and set their correct directories in preparation for the next ‘Symlinks‘ step.
The PHPUnit developer uses Github as their version control repo, which is good, but sucks when trying to download packages, as the actual files are abstracted by an intermediary PHP script. What I ended up having to do was download to my local machine, then secure copy them over to the server. Once you have the source files on the server, run the following steps:
$ -> cd ~/projects/vendor $ -> mkdir phpunit $ -> cd phpunit $ -> cp ~/sebastianbergmann-phpunit-3.6.7-9-gf5e159b.zip . $ -> unzip sebastianbergmann-phpunit-3.6.7-9-gf5e159b.zip # The unzip will result in a weird hash, so I just rename it to # the correct version based on the original file name $ -> mv sebastianbergmann-phpunit-f5e159b/ 3.6.7-9 # Don't need source file anymore $ -> rm *.zip # Now, we have phpunit locked into a version which we can quickly # glean when we inspect symlinks to this library # Lets get the other necessary packages $ -> mkdir lib $ -> cd lib $ -> pwd /home/mpurcell/projects/vendor/phpunit/lib # Setup base dirs for phpunit add-ons $ -> mkdir codeCoverage fileIterator timer # Now lets copy our add-ons into their respective dirs $ -> cp ~/sebastianbergmann-php-code-coverage-1.1.1-14-gdc2a15a.zip codeCoverage $ -> cp ~/sebastianbergmann-php-timer-1.0.2-5-gb352e92.zip timer # File iterator lib allows you to set a root directory, and phpunit will traverse the directory looking for unit tests $ -> cp ~/sebastianbergmann-php-file-iterator-1.3.1-1-gbbaab46.zip fileIterator # Now that we have the files in our lib directory, lets get them setup $ -> cd codeCoverage $ -> pwd /home/mpurcell/projects/vendor/phpunit/lib/codeCoverage $ -> unzip sebastianbergmann-php-code-coverage-1.1.1-14-gdc2a15a.zip $ -> mv sebastianbergmann-php-code-coverage-dc2a15a/ 1.1.1-14 $ -> rm *.zip $ -> cd ../fileIterator $ -> pwd /home/mpurcell/projects/vendor/phpunit/lib/codeCoverage $ -> unzip sebastianbergmann-php-file-iterator-1.3.1-1-gbbaab46.zip $ -> mv sebastianbergmann-php-file-iterator-bbaab46/ 1.3.1-1 $ -> rm *.zip $ -> cd ../timer $ -> pwd /home/mpurcell/projects/vendor/phpunit/lib/codeCoverage $ -> unzip sebastianbergmann-php-timer-1.0.2-5-gb352e92.zip $ -> mv sebastianbergmann-php-timer-b352e92/ 1.0.2-5 $ -> rm *.zip
Ok, now you should have phpunit and it’s core libraries “installed” on your server. In this context, installed is a loose term, as the code isn’t really installed, it’s now available to be hooked into from your testing environment.
Symlinks
As with any 3rd party APIs introduced into your application, it is always best to abstract away version numbers, so you don’t force your application to require files with version numbers in a file’s uri. For example:
// Good require_once 'path/to/ThirdParty_Vendor/Api/Class.php'; // Bad require_once 'path/to/ThirdParty_Vendor-1.1.1-9/Api/Class.php';
Why does this matter? Because, when a new version of the file is released, and you upgrade, you won’t have to update all the code references. Instead, just change the related symlink and your application will work as before, except with the newer version of the file.
So lets make the PHPUnit libraries available to our testing environment:
$ -> cd ~/projects/core/test $ -> mkdir lib $ -> cd lib $ -> pwd /home/mpurcell/projects/test/lib $ -> ln -s ~/projects/vendor/phpunit/3.6.7-9/PHPUnit $ -> ln -s ~/projects/vendor/phpunit/lib/codeCoverage/1.1.1-14/PHP PHPCodeCoverage $ -> ln -s ~/projects/vendor/phpunit/lib/fileIterator/1.3.1-1/File PHPFileIterator $ -> ln -s ~/projects/vendor/phpunit/lib/timer/1.0.2-5/PHP PHPTimer
Ok, now we have hooks to phpunit libraries within our testing directory, onto the next step.
Bootstrap
A bootstrapper is meant to prepare a library for usage, and that’s exactly what we are going to do. This bootstrapper will setup include_paths along with a few other settings so we can use run our tests correctly. The code is posted here, otherwise it’s too hard to read in wordpress.
Autoloader
Here’s where the magic lies. This autoloader will know how to map files that PHPUnit is requiring, to their symlinked location. It’s pretty easy to follow, all we do is check for a namespace footprint (PHP_Timer), and if it exists, re-map it to our symlink (PHPTimer). The code is posted here, otherwise it’s too hard to read in wordpress.
Changes to PHPUnit Source code
Now for the ugly. It is NEVER a good idea to edit 3rd party (vendor) code directly, otherwise you update and forget about the changes and your app goes to hell. However in this instance I had no choice, and fortunately I was able to limit the change to just one location in the PHPUnit source code. It is a trivial change, but you should add a README to your ~/projects/vendor/phpunit directory to remind yourself to make this change for future upgrades (assuming the lead developer doesn’t change it himself).
My README file:
ATTENTION!
When upgrading phpunit, 1 change must be made to ensure unit tests work
Why: This allows us to use our own autloader
File: PHPUnit/Util/GlobalState.php
Line: 98
Change:
From: public static $phpunitFiles;
To: protected static $phpunitFiles;
This change makes it so we can override the singleton $phpunitfiles. If we don’t over-ride it, then PHPUnit will attempt to use it’s built-in autoloader, which doesn’t work with our setup. By setting the scope from protected to public, we can over-ride the value with an empty array, as the conditional used in source code check is ‘ === NULL’.
phpunit executable
Now we need to create the phpunit executable which will kick off the unit testing. When I was going through the PHPUnit source code, I noticed that the PEAR installer was just renaming the phpunit.php to phpunit. So, lets copy the phpunit executable which came with PHPUnit to our test directory and make a few minor changes:
$ -> pwd /home/mpurcell/projects/core/test $ -> cp ~/projects/vendor/phpunit/3.6.7-9/phpunit.php .
Now, make the following changes:
#!/usr/bin/env php
// Bring in our bootstrap file
require_once substr(__FILE__, 0, strpos(__FILE__, '/test')) . '/test/Bootstrap.php';
// Set our main define
define('PHPUnit_MAIN_METHOD', 'PHPUnit_TextUI_Command::main');
// Remember we set the scope of $phpunitfiles to public?
// Now we can set it to whatever we want, which is just an empty
// array, which allows our autoloader to handle loading files
PHPUnit_Util_GlobalState::$phpunitFiles = array();
// Kick this pig
PHPUnit_TextUI_Command::main();
Home Stretch
Finally, we made it. Now, we can call on the phpunit executable and run tests. First lets create a sample test:
$ -> pwd /home/mpurcell/projects/core/test $ -> touch CoreTest/ArrayTest.php
Now lets add an easy test:
<?php
class CoreTest_Api_Array_PackageTest extends PHPUnit_Framework_TestCase{
public function testCount()
{
$this->assertCount(2, array('foo', 'bar'));
}
}
Now, run the test:
$ -> pwd /home/mpurcell/projects/core/test $ -> ./phpunit/CoreTest/
Now your ArrayTest test should run (and pass). Notice that we passed a directory to the ./phpunit executable, you can pass a directory, or a specific test to meet your needs. The PHPFileIterator we setup will parse directories for any files ending in *Test.
Congratulations
Hopefully this article wasn’t too hard to follow, and your unit tests are running though PHPUnit, as if you installed through PEAR. If you wouldn’t mind taking the time to contact the lead developer, and let him know that he should drop PEAR and run an autoloader/bootstrap combo like many other PHP libraries/frameworks, I would greatly appreciate it. He has done a good job, but we need to make PHPUnit easier to install and set-up so others don’t get discouraged and give up unit tests altogether.
Linux – PHP – Apache – HTTP Error 500
0Had a weird issue where my demo server was throwing 500 error when a request was made. I spent time digging into my nginx configs to see if there were a issue, once I was able to determine it was not nginx, I started tearing apart my apache vhosts to see what the issue was. It was tough to track down because neither nginx nor apache error logs were logging anything out of the ordinary.
I came across a serverfault.com post where someone suggested using the following command after making a request:
find /var/log/ -mmin -1
This command will return any files whose modtimes are less than a minute old. When I issued the command I noticed that my PHP error log was listed, so I tailed the error log and issued another request. Sure enough, an exception was being thrown because my bootstrap file for my core library could not make a needed database connection.
Now that I was aware that an exception was causing my apache server to issue a 500 response, I still couldn’t figure out why it wasn’t outputting the exception message. I started digging around my php.ini file and found that I had set `display_errors=off`. With display_errors set to off, apache will issue a 500 response rather than output the exception, which is a good thing b/c the exception message had some database connection information.
So if you are setting up an apache/php server and it’s throwing a 500 response, check the php error log too, you may have the same setup.
Also, I read that this type of behavior will occur if the php.ini file could not be read.
Symfony – Standard API for Logging Using __callStatic
0Last year I wrote an article on how to write a simple, standard logging class for Symfony (http://melikedev.com/2010/08/25/symfony-standard-api-for-logging/). This article will demonstrate how do to the same functionality in only a few lines of code using __callStatic(). For more info on __callStatic() check out http://melikedev.com/2011/10/31/php-__callstatic-method-overloading/
Before:
// located @ /lib/MyCustom/Api/Log.php
class MyCustom_Api_Log
{
/**
* Wrapper method for getting a symfony logger object
*
* @return object
*/
public static function getLogger()
{
return sfContext::getInstance()->getLogger();
}
/**
* Wrapper method for logging debug message
*
* @param string $message
*/
public static function logDebug($message)
{
self::getLogger()->debug($message);
}
/**
* Wrapper method for logging an error
*
* @param string $message
*/
public static function logError($message)
{
self::getLogger()->err($message);
}
}
// Example Usage
MyCustom_Api_Log::logError('Oh noes something went wrong');
After:
// located @ /lib/MyCustom/Api/Log.php
class MyCustom_Api_Log
{
/**
* Wrapper method for getting a symfony logger object
*
* @return object
*/
public static function getLogger()
{
return sfContext::getInstance()->getLogger();
}
/**
* Magic method for logging all supported log levels
*
* @param string
* @param array
*/
public static function __callStatic($level, $params)
{
// Only concerned with message at this time
// However any number of params can be passed in
$message = shift($params);
self::getLogger()->$level($message);
}
}
// All these calls are made to __callStatic, so no need for individual methods
MyCustom_Api_Log::err(array('Oh noes something went wrong'));
MyCustom_Api_Log::warn(array('Oh noes something went wrong'));
MyCustom_Api_Log::notice(array('Oh noes something went wrong'));
PHP – __callStatic() – Method Overloading
0In case you weren’t aware, PHP 5.3.x+ has provided a new method known as __callStatic(), which works the same as __call(), but as you may have guessed, it can be called statically. You can get more info from http://us.php.net/manual/en/language.oop5.overloading.php
Example:
class Foo
{
public static function __callStatic($name, $params)
{
return 'Called ' . $name . ' with ' . implode(',', $params);
}
}
echo Foo::bar(array('This is my message));
Linux – Apache – Php – php_network_getaddresses – Name or service not known
0Came across an issue where I was getting “SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known” errors when one of my applications was trying to establish a connection to a MySQL db.
In my /etc/resolv.conf file I didn’t have the domain with the host DNS information, so I added it. At this point I was able to ping the host via command line, but was still getting the errors. After some Googling I came across http://albertech.net/2011/05/fix-php_network_getaddresses-getaddrinfo-failed-name-or-service-not-known/ where the author indicated that if you can ping the host via command line but are still getting the errors, try restarting Apache. So I followed his suggestion, restarted Apache, and it worked like a charm.
Symfony – PHP – Possibly Forking 1.x so Invested Companies don’t Lose Millions
13So there seems to be some rumblings from within the Symfony world that the dev team “may” fork 1.x in order to continue maintenance beyond the announced 2013 “end of life” window, rather than ending active development on 1.x and “hoping” everyone shifts to Symfony 2.
Source blog post @ http://test.ical.ly/2011/06/22/quo-vadis-symfony-1-4/ (lol @ FQDN).
I posted a comment on the blog, which is copied verbatim here in case it gets lost in the static.
It’s amazing that we are even having this type of discussion. We opted to use Symfony back in 0.x for some high traffic projects. Our company spent a lot of time and money training the dev team to correctly use the framework. And it wasn’t just the initial learning of moving from a hodge-podge code-base to MVC code-base, we also had to relearn new ways of doing things with some minor revisions (1.0 -> 1.2 forms for example). How much time was spent learning how to get the form system to work with some highly customized forms? A lot.
And now here we are on 1.4. Content and happy with the throughput the dev team is able to maintain. But now Symonfy says that 2.0 will be released and is completely different than 1.x? How is that fair to the companies, individuals, and hobbyists who have sunk time over the last four years ramping up to a point where we can take advantage of RAD (rapid app development) approach that Symfony provides? Granted, Symfony did extend the maintenance window of 1.x to 2013, but to completely abandon a great framework is callous and irresponsible, to be frank.
When 1.x is no longer supported, the dev team will just have to maintain 1.x as if it were our own. It’s tragic, but financially, we can’t keep paying the costs of heavy refactors incurred with new major revisions. Will we be having this discussion again in three to four years, when Symfony 3 is slated for release? When companies during that time period would have made the brutal choice to shift from Symfony 1 to Symfony 2, only to be abandoned again?
IMO, I think Symfony should follow closer to the examples set by industry giants such as Apache, MySQL, and PHP with regards to major revisions. Make them available, but be gentle about the transition. Leave it up to the companies to determine whether or not to upgrade.
Thanks and I <3 Symfony.
My question is, what do other Symfonians think of the upcoming changes?
PHP – Deep Clone an Object
0Cloning an object in PHP is advantageous at times when you want to make a copy of an object so you can alter the copied object’s properties without affecting the original object’s properties.
Reading the docs on php.net, cloning an object is pretty straight forward:
class Car
{
public $type;
public $color;
}
$myCar = new Car();
$myCar->type = 'sports';
$myCar->color = 'red';
$herCar = clone $myCar;
$herCar->color = 'pink;
var_dump('My car | style: ' . $myCar->style . ' | color: ' . $myCar->color);
// Outputs string(35) "My car | style: sports | color: red"
var_dump('Her car | style: ' . $herCar->style . ' | color: ' . $herCar->color);
// Outputs string(37) "Her car | style: sports | color: pink"
// Notice her style is same as mine, but her color is different
Pretty straight forward right? I was able to instantiate an object, set style and color member vars, then clone $myCar object, then set a different value for color member var on cloned object.
As php mentions, references to first level values are maintained, but what about if your object returns another object? Consider the following code:
<pre><?php
class Car
{
public $style;
public $color;
protected $tire;
public function getTire()
{
if (!($this->tire instanceof Tire)) {
$this->tire = new Tire();
}
return $this->tire;
}
}
class Tire
{
public $size;
public $rating;
}
$myCar = new Car();
$myCar->style = 'sports';
$myCar->color = 'red';
$myCar->getTire()->size = '20';
$myCar->getTire()->rating = 'Z';
$herCar = clone $myCar;
$herCar->color = 'pink';
$herCar->getTire()->size = '18';
$herCar->getTire()->rating = 'R';
var_dump('My car | style: ' . $myCar->style . ' | color: ' . $myCar->color);
// Outputs same as above
var_dump('Her car | style: ' . $herCar->style . ' | color: ' . $herCar->color);
// Outputs same as above
var_dump('My car tires | size: ' . $myCar->getTire()->size . ' | rating: ' . $myCar->getTire()->rating);
// Outputs string(35) "My car tires | size: 18 | rating: R"
// Notice that my tire settings were over-ridden by her tire settings
var_dump('Her car tires | size: ' . $herCar->getTire()->size . ' | rating: ' . $herCar->getTire()->rating);
// Outputs string(36) "Her car tires | size: 18 | rating: R"
// This is what we expected
As you can see in the above example, any ‘nested’ objects are ignored via the clone call. Thanks to Wayne Haffenden, there is a way to resolve this situation, you can view his blog post @ http://www.waynehaffenden.com/Blog/PHP-Deep-Object-Cloning. He came up with a clever way of ensuring that any nested objects are also cloned by serializing the object then unserializing upon return. Consider the following code:
class Object
{
// Can't call it clone b/c it's a reserved word
public static function cloneIt($object)
{
return (is_object($object)) ? unserialize(serialize($object)) : null;
}
}
// Instead of making call to php clone, make call to new class::clone
$herCar = Object::cloneIt($myCar);
After adding the code to the original script, when you rerun it you get the following output:
string(35) "My car | style: sports | color: red" string(37) "Her car | style: sports | color: pink" string(35) "My car tires | size: 20 | rating: Z" // Eureka! string(36) "Her car tires | size: 18 | rating: R"
As you can see from the output, we were able to maintain our original tire settings which demonstrates that we were able to perform a ‘deep’ clone where nested objects are cloned as well.
Thanks to Wayne for posting his findings, and I hth.
PHP – Upgrade to 5.3.x – Things to Look out for
2When upgrading to php 5.3.x there will be some issues to look out for before you deploy to production, this thread is meant to contain those issues for easy reference.
date.timezone (php.ini)
PHP developers decreased the error level for not initializing timezone correctly, adjusting from strict to warning. Chances are you will start to see some warnings surrounding your date() calls. To rectify this, simply make the following change to your php.ini file:
date.timezone = America/Los_Angeles
Make sure the entry is uncommented, and set the value to whichever value you need (a list can be found @ http://us3.php.net/manual/en/timezones.php)
__toString()
In PHP 5.3, the magic __toString() methods no longer allow you to pass in arguments, which makes sense on some level, but does reduce flexibility.
PHP – Array – Shuffle() gotcha
0Be careful when using PHP’s shuffle() function, it will reset your keys. Why does this matter? What if you had an associative array? Or, if you populate arrays with DB data and set an array key to the primary key of a db row.
There are some user contributed functions which will maintain existing keys, but IMO PHP dev team should add a MAINTAIN_KEYS flag or something so it can be used on the source level and not the PHP level.
