PHP

warning: Creating default object from empty value in /home/greg/public_html/modules/taxonomy/taxonomy.pages.inc on line 34.

Correctly Naming Methods

Method and variable naming is known to be one of the most difficult aspects of software development. The idea is to choose methods that correctly describe the intended purpose of the variable or method to other developers.

This is why developers with good memories sometimes make poor developers. If you can always remember the precise purpose of your variables regardless of the name you choose then you don't have to choose good names.

Often the solution is right in front of you. Here is a very slightly modified example from a well known PHP framework:


/**
* Determine if an instance exists.
*
* @param string|array
* Example: array('canonicalName', 'requestName')
* @param bool $checkAbstractFactories
* @param bool $usePeeringServiceManagers
* @return bool
*/
public function has($name, $checkAbstractFactories = true, $usePeeringServiceManagers = true)
{ ... }

Symfony2 Controllers as Services - Remember to change your routes

If you upgrade a controller to be a service you need to change the entry in the routing.yml for each action:

Change

defaults: { _controller: SomeBundle:Api:queueFormSubmissionToService, _format: ~ }

to

defaults: { _controller: someservice.someapi.controller:queueFormSubmissionToServiceAction, _format: ~ }

If you don't change the route then the controller is not run as a service and any dependencies are not injected.

The "Action" part of the controller action method name is only required if running the controller as a service. If you forget it you get the slightly obscure error message:

"The controller for URI ... is not callable."

Unit testing in Zend Framework 2

Unit testing fails in ZF2 Beta 1 if you download the tarball. Use the git repository, this contains the Bootstrap.php and phpunit.xml which are missing from the tarball.

It would be very useful to have symbollic links from the source directories to the test directories so you can quickly update tests and see the interfaces in action.

Lack of proper class method overloading is holding PHP back

The lack of proper class method overloading as found in other languages like Java and C# is holding back PHP's development as a first class language and leading to very clunky code. Here is an example based on Zend Framework 2's Zend\Tag\Item class:
   public function __construct($options)
    {
        if ($options instanceof \Zend\Config\Config) {
            $options = $options->toArray();
        }

        if (!is_array($options)) {
            throw new Exception\InvalidArgumentException('Invalid options provided to constructor');
        }
       $this->setOptions($options);
    }
And here is how it should look:

   public function __construct(\Zend\Config\Config $options)
   {
      $this->__construct($options->toArray());
   }

   public function __construct(array $options)
   {
      $this->setOptions($options);
   }
The latter is so much more elegant but sadly it is not going to work in PHP any time soon.

I Want a goto and I Want it NOW!

This evening I was working on testing some code that is called by AJAX. It returns a string which is either "OK", if everything is fine or something else, if it isn't. If something else is returned the user is alerted.

So to test this I pull it into my test framework - except that the calls to "exit" mess everything up and make the test framework exit too. wget and curl are too clunky so, proceeding with the elegant solution I need to change the code so that instead of exiting it jumps to the end of the script, that way it will return to the test suite even if there is a problem. I'm sure PHP has a GOTO so go look in the documentation - its 5.30 - nightmare!

So instead move the AJAX code into a function and change the exit calls to be returns. Ta-da unit testable AJAX.

Still this is a milestone. The first time in my life I ever wanted a goto.

PHP exec unlike other execs

In PHP the exec call is part of a family of calls that can run commands on the server. These include system, passthru and shell_exec. They all offer slightly different functionality and all require careful security audit to ensure that you do not allow unscrupulous users of your site access to the operating system.

An empty abstract class can be useful

I've been working on a system that takes different types of things in an order. The order system is split up by the type of the thing beng ordered, which isn't very clever but historically that's the way someone wrote it so we just have to live with it, refactoring the code and data is not an option.

In modelling the data for the differnet types of thing being ordered it turns out to be useful to have an empty abstract class:

abstract class AbstractOrderItem implements Model {
// Add a constructor to initialise the model
}

Now order items extend AbstractOrderItem:

class CarOrderItem extends AbstractOrderItem ...
class PlaneOrderItem extends AbstractOrderItem ...
class TrainOrderItem extends AbstractOrderItem ...

Now it you want to handle a list of OrderItems that's fine:

    function paintBlue(AbstractOrderItem $item) {
      $item->color='blue';
      $item->save();
    }
  

Simple PHP Copy vs Clone Example

Assigning object instances results in assigning references:

<?php
class s {
  public $state = 1;
}

$s = new s();
$t=$s;

$s->state=4;

echo "s state ".$s->state."\n";
echo "t state ".$t->state."\n";

s state 4
t state 4

Using clone creates a shallow copy of the object. (Add a __clone() method to define your own deep copy. See http://php.net/manual/en/language.oop5.cloning.php)

<?php

class s {
  public $state = 1;
}

$s = new s();
$t=clone $s;

$s->state=4;

echo "s state ".$s->state."\n";
echo "t state ".$t->state."\n";

s state 4
t state 1

Note: Use debug_zval_dump() to see the reference count for a variable.

When to Refactor

Today I found a class file that had been created with 1161 lines in it. Files this big should immediately ring alarm bells because the class they contain is almost certainly doing too many things. Class files this big are inevitably difficult to test and sure enough the test file was about 1700 lines.

Some guidelines on when to refactor:

  • The class is doing more than one thing.
  • You can't get a method on one screen.
  • There is poor on non-existent documentation.
  • There is poor or no unit testing.
  • Class names and method names fail to adequately describe what the classes and methods do.
  • Methods have too many parameters. In the case of the class above the constructor took (Resource, Resource, pointer to function, Resource) which isn't necessarily too many its just a little strange and that strange smell points to refactoring.

Why Stuff2send.com was Such a Success

In just 7 weeks of November and December 2008 I wrote the initial code for http://www.stuff2send.com, a site which allows people travelling round the country to earn extra money by delivering packages as part of their journey. The site, which included Google Map and PayPal integration, went live on time and within budget and support and development was then taken over by the brilliant Tom and Lisa at 18aproductions.com.

Comments: Who are you talking to?

I've always thought that the best programmers have the worst memories. The reason is simple. If you have a bad memory you have to write your code in a way that you will understand it in the future. If you have a good memory you can do what you like, give variables stupid names, make the code as unreadable as you like - you can rely on your memory to help you out when you revisit the code.

There is something of an art to writing and commenting code, because its a team game. Not only does your code have to work but it is essential that anyone who reads it can understand it, without it taking them longer to understand it than it took you to write it in the first place. If you write code that nobody else can understand then there is really no point in writing the code. All that can be done is to wrap some unit tests round your code and refactor it to literacy.

Unit tests are a good way of "documenting" code. At least they give usage examples which can help enormously.

How method scope affects functionality

Its not always obvious which method gets called when overloading methods with different access controls and calling them from the parent class. Consider:

<?php
class C1 {
  public function __construct() {
    $this->go();
  }

  private function go() {
    echo "In the C1::go()\n";
  }
}

class C2 extends C1 {
  public function __construct() {
    parent::__construct();
  }

  protected function go() {
    echo "In the C2::go()\n";
  }
}
$c=new C2();


The output is "In the C1::go()"

Now consider:

class C1 {

Another thing you can't do with PHP

I want to run a test that should take less than a second but could perhaps get into a longer / infinite loop, so I want to set a timer interrupt to go off in 1 seconds time and handle the exception it generates to force a test failure.

What is the probability of being able to do this with PHP?

Building PHP from scratch - Tests Fail

I've just built 2 copies of PHP 5.2.9 from scratch on different linux versions (with very slightly different configurations).

In the first case make test returns 40 errors (from 6529 tests) (0.6% of tests which were run fail) In the second case 54 (0.8%) of 6550 tests fail.

That's a lot of tests failing. It should build cleanly.

Did all tests run when 5.2.9 was released? Who knows. Maybe its just developers failing to run and update tests when code is updated. Maybe its even more serious.

Mimicing overriden methods in PHP

PHP's lack of support for overriding methods is a real performance hog. You have to work out what the programmer meant by the types of the parameters sent to the method manually. All this takes time and has to be done every time the method is called.

You end up with code that looks like this:

/**
* I want to do something like calling myfunc(new Instance("thingstring")) or just
* myclass("thingstring");
*/
public function myfunc( $instance ) {
if (is_string($instance)) $instance=new Instance($instance);

Syndicate content