Refactor 2015

Major changes are taking place, and none of you will know the difference unless you look through my source code. Well, there are some visual changes that I'd like to make as well, but it's mostly rethinking my existing code and how to test it, make it easier to understand and extend, and look like what well written code is supposed to look like. Standards are immensely important. Having PSR's is a great thing! I just wish that it had been done better. That's not to say that I know better than an entire group of prominent developers, but there are cases where PHP-FIG's decisions are clearly placing visual preference above performance, compatibility, and simplicity. Also, I'm taking sides with the original developers of PHP on some of these issues.

Formatting

There are a lot of minor changes that I'm making that have almost no impact whatsoever on either functionality or performance. If anything some of the formatting changes result in a file that is a few characters longer and, therefore, is read slightly more slowly, but the difference there would only be in nanoseconds!

See PSR-2.

But, when a group of prominent developers state that such is the way things ought to be done, I'm open to making a few changes if for nothing other than doing my part in setting  a standard to how PHP is to be writtern. Personally, I think how I had been doing things looked better, and in many areas I think most developers prefer my old way.

function my_function($args) {
if($args) {
// Do some stuff
}
else {
// Do something else
}
}
V.S.
function my_function($args)
{
if ($args) {
// Do some stuff
} else {
// Do something else
}
}
I think having the opening curly brace on the next line is a waste of a line, is less legible, and somewhat detaches the function name from its body… Like setting a variable with its name on one line, an equals sign on the next, and finally it's value on yet another.

Another difference which I could not really show is indenting with 4 spaces instead of a single tab. This is one that I have absolutely no intention of changing. I will continue indenting with tabs… It's just a necessity if you make frequent use of vim. Tabs for indening, spaces for alignment. Best of both worlds!

Naming Things

The subtle and insignificant differences pretty much ended with the code structure standards. Sometimes how you choose to name things isn't really that important so long as there is a standardized method for coming up with those names, but there are cases where having a bad standard for naming things completely breaks things at a functional level, and PSR-4 is guilty of exactly that!

These are the areas where I strongly disagree with PHP-FIG:

All class names MUST be referenced in a case-sensitive fashion.

...

The terminating class name corresponds to a file name ending in .php. The file name MUST match the case of the terminating class name.

PSR-4 — Autolaoder

Quick quiz…

// XML/MyClass.php
namespace XML;
class MyClass
{
public $var = 'Hello';
}

// Xml/MyClass.php
namespace Xml;
class MyClass
{
public $var = 'World';
}

$test1 = new \XML\MyClass();
$test2 = new \Xml\MyClass();

echo $test1->var . ' ' . $test2->var;

What is echoed?

If you guessed Hello World, you would be wrong!

The correct answer is Hello Hello.

Why? Because, whatever PHP-FIG says, namespaces and class names in PHP are case insensitive, so Xml/MyClass.php is never even loaded. XLM\MyClass is simply reused. An autoloader is never even called for the second class because PHP considers the resouce to have already been loaded.

Not convinced? Try importing these classes onto Windows. They cannot coexist! Windows and Mac filenames are case insensitive, whereas Linux, Unix, etc. are case sensitive. The developers of PHP, to prevent mistakes in case sensitivity, decided to make classes case insensitive by requiring all files to be lower case by default and making classes and namespaces case insensitive.

Need more?

The built in autoloader in PHP is spl_autoload. Setting it up is as easy as

set_include_path(__DIR__ . DIRECTORY_SEPARATOR . 'vendor' . PATH_SEPARATOR . get_include_path());

spl_autoload_register();

// Optionally allow classes with a variety of extensions
spl_autoload_extensions('.php,.class.php');

Users of Composer might now complain that using Composer's autoloader (which is PSR-4 compliant) is as simple as

require 'vendor/autoload.php';

Yep, but you could move this autoloader to a separate file and include it as well, but you're not counting how much code is in vendor/autoload.php or all of the other classes it contains. Aside from that, I guarantee you that no autoloader in PHP will ever be as fast as spl_autoload. The reason is that any custom autoloader will be written in PHP, which is a scripted language and therefore slower than PHP itself, which is written in C, which is a compiled language. Sure, you could completely screw up the above configuration and have the autoloader searching though entire drives looking for a class, but that could really only happen with a deliberate effort.

And no, the point of all of this is not to tell anyone to stop using Composer or any custom autoloader of their choice, but just to say that there is an extremely simple, better performing, and more compatible alternative that is almost impossible to screw up, and anbody running PHP > 5.3 has it already without needing to download or even enable it (aside from the code I gave above).

This relates to my post here about refactoring my code because I can only pick two of the following:

  1. Preserve compatibility by using the native spl_autoload function.
  2. Somewhat or at least mostly comply with PSR-4 conventions.
  3. Take advantage of Composer and all of the packages it provides.

I could entirely break PSR-4 standards and call all of my classes in lower case (in the classes themselevs, this shouldn't matter). I really don't want to do that though… It wouldn't look right to the point where it would often times be difficult to read. Aside from this option, however, I get to choose between Composer and spl_autoload.

I guess I could use both, but that would just be horribly inefficient. That would require for one to fail before the other is even attempted.

More to come soon. I'm trying to restructure my class directory, break some more complex classes down into abstracts or traits, implement interfaces, and a whole lot of other improvements that will really make things better in the future.

Oh, and I'm intending on implementing shared interfaces between production classes and my testing classes, just to make the tests that I run that much more valid.