12 January 2017

Traits in PHP

What are traits?

Image: Pixabay
Let's start by understanding what traits are and how they're useful.  We'll move on to code examples straight after that.

Traits are not unique to PHP and are available in other languages too.  They provide a way to extend the functionality of a class.  A trait will have methods to implement this functionality and make these available as if they had been defined in the class itself.

In other words traits are flattened into a class and it doesn’t matter if a method is defined in the trait or in the class that uses the trait. You could copy and paste the code from the trait into the class and it would be used in the same manner.

The code that is included into a trait is intended to encapsulate reusable properties and methods that
can be applied to multiple classes.  Traits group functionality in a fine-grained and consistent way and allow you to reuse this functionality without requiring inheritance.

I mentioned before in the lecture on inheritance that PHP does not support inheriting from multiple classes.  Traits offer a way to provide similar functionality and offer a way to avoid the so-called "diamond problem" that is introduced with multiple inheritance.

Defining and using traits

Traits are very easy to use and there are lots of places that they can be useful.  I'm going to run through an example that defines a trait that implements singleton functionality.



We define the trait using the `trait` keyword followed by the name we want to use.  We can declare variables in the trait as well as functions.  

If you look at the code example you'll see that my Singleton trait has got all of the implementation requirements for the singleton pattern.

I've defined a private static variable called $instance in the trait.  Although it's private in the trait remember that the code gets flattened when a class uses the trait and so the class would be able to access it.  If that's not clear just imagine copying and pasting the code in the trait straight into your class.

The trait also includes a static function called getInstance that returns a singleton instance of the class.

I'm using the trait in the class called MyClass.  The `use` keyword indicates that we're using the trait.  You might already be familiar with the `use` keyword as a way to import namespaces.  When we import a namespace the `use` keyword is outside of the class, if it's inside the class it indicates that we're using a trait.

Now we're able to call the `getInstance()` static method on the class.  This method is defined in the trait but we're using the trait in the class so it's available to be used as part of the class.

We're able to overwrite the constructor method in the trait.  In this example I've set the constructor to private so that you are not able to instantiate the class but rather have to use the singleton method to get an instance.  Trying to create a new instance of MyClass generates a fatal error because `__construct()` is public.

Now we're able to easily give a class all of the functionality it needs to be turned into a singleton without interfering with the inheritance hierarchy.

Namespacing Traits

So far we've seen how to declare and use a trait.  What happens if we want to use a name for our trait that has already been using elsewhere in our code?  It might happen for example that one of the libraries we use has already defined a trait named Singleton.

Traits, just like classes, can be namespaced.  The name of a trait must be unique within its namespace otherwise PHP will throw a fatal error.

Just as with classes if you're wanting to use a trait in a namespace other than your own then you'll need to provide a full reference to it.

Inheritance and Method precedence

Traits may not inherit from other traits or classes but you can nest one trait inside another.

Okay so inheritance is simple, but what about situations where a class and trait have a method with the same name?  Which method will take precedence and be run when we try to call it?

Methods declared in a class using a trait take precedence over methods declared in the trait. However, methods in a trait will override methods inherited by a class.

Expressed more simply, precedence in traits and classes is as follows:

CLASS METHODS first, then TRAIT METHODS, and lastly INHERITED METHODS.


Conflict resolution in traits

What happens if your class is using two traits and they both declare a method with the same name?  

PHP requires you to explicitly resolve these sort of conflicts and will throw a fatal error if you try to use two traits that each define a method of the same name.

There are two approaches to resolving name conflicts between traits.  You can either tell PHP not to use a method that is defined in one of the traits or you can tell PHP to use an alias for a method in one of the traits.  Put shorter, you can exclude a method or alias a method.

In order to exclude a method you use the `insteadof` operator which tells PHP to use a method from one trait instead of the method from the other.

In order to alias a method you use the `as` operator to provide an alias to use.  The only trick here is that it's not enough to just specify an alias, you must *also* exclude the original method from being used.

Right, so we've got that we can either exclude or alias a method to resolve naming conflicts between traits.  Lets look at some code to see how it works.



First we're going to create two traits.  We're going to define two methods in each trait and name them such that they'll create naming conflicts.

We're going to use the traits in a class which we're calling DomesticPet.  Because the methods in the traits have conflicting names we're going to have to resolve these conflicts.

We resolve the conflicts in a code block that follows the `use` keyword.  We have to resolve every conflict so lets step through this now.


Replacing methods with "insteadof"

For the first method, `makeNoise()`, we're going to simply exclude the method from the Dog class.  We would do this if we only ever want to use the Cat method and expect never to use the Dog method.

In order to do this we're going to tell the class to use the makeNoise function declared in the Cat trait instead of the one in the Dog trait.  

We use the `insteadof` keyword to tell PHP to use the method declared in the Cat trait instead of the one in the dog class.

Whenever the `makeNoise()` function is called PHP will know that you mean the one defined in the Cat trait and you won't be able to access the one from the Dog trait.


Aliasing methods with "as"

For the second method, `wantWalkies()`, we're going to rather alias the method in Cat.  We would do this when we want to be able to use both methods in our class.

You may have already noticed in the code sample that there are three statements in the conflict resolution block but only two functions that need to be resolved.  

Remember that earlier I said the trick with aliasing methods is that it's not enough to alias by itself and that you must also exclude the method you're aliasing?  

We're going to need two statements to alias our method.

First we're going to tell PHP to give the method an alias that we can use to reference it.  Then we're going to tell PHP to exclude the original method.

In order to alias the method we use the `as` keyword.   We're going to give the `wantWalkies()` method an alias of `kittyWalk()`.  By doing this you will be able to call the `kittyWalk()` function in your class.  This isn't a new function, it's just an alias for the function in the Cat trait.

Right after we alias the function we go ahead and tell PHP to use the Dog `wantWalkies()` method instead of the Cat trait.  If we left out this step we would still have a naming conflict.


The end result

We used the Cat `makeNoise()` function instead of the Dog one.  When we call the `makeNoise()` method on the object we are referencing the cat function and so the output is "Purr".

We aliased the Cat `wantWalkies()` method as `kittyWalk()` and so when we call the `kittyWalk()` method on the object we are referencing the aliased Cat method and the output is "No Thanks".

Because we are aliasing the Cat `wantWalkies` method we also had to use the Dog method instead of the Cat method.  Therefore when we call the `wantWalkies` method on the object we are referencing the Dog method and the output is "Yes Please".


Visibility

You might want to change the visibility of a method that you're using from a trait.  This is done by using the `as` keyword.  

Lets look at a simple example before we take a look at the complications.

We can set an aliased method as protected by inserting the keyword `protected` between `as` and the name we're aliasing it as.  For example we can say,

Dog::wantWalkies as protected doggyWalk;

Changing the visibility of a replaced method is slightly more complicated.

In order to change the visibility of a replaced method we need to use two steps.  First we use `insteadof` to replace the method and then in the next statement we stipulate that it must be protected.

Lets look at this in code.  



In the first line inside the trait use block I'm aliasing a function and also changing its visibility.  The keyword `protected` appears between `as` and the aliased name.  If I were to try and call `doggyWalk()` from outside the class I would receive an error because it is protected.

I've commented out the second line because it's an example of what doesn't work.  The rule of thumb seems to be that you can't use both `as` and `insteadof` in one single statement.  You need to split them into separate statements as I do in the last two lines of the use block.

Tip

24 September 2016

Connecting to Elixir web channels from the Angular 2 quickstart application

I am busy learning Elixir, a language that adds syntactic sugar to the awesomely scalable and concurrent Erlang language.  The "go to" framework in Elixir is Phoenix and I'm busy writing my "hello world" application which will serve up data across a web channel.

I followed the Typescript version of the Quickstart guide for Angular 2 (here).  I really like what I've seen of Typescript so far.  Dependencies are easy to manage and the ability to define interfaces is a good sign of how well structured the language is.  I think Satya Nadella should be made an open source hero, if such an award exists.

Anyway, what I wanted to do was get my Angular 2 application to be able to connect to the Elixir server channel and send a request to listen to a particular stream.  The idea is to use the Actor concurrency model (explained brilliantly in "The Little Elixir & OTP Book") to start up a new process whenever a request for a stream arrives.  This article focuses on setting up the Angular 2 connection.

The official Phoenix javascript client is packaged up and can be installed with "npm install --save phoenix".  Once it's installed we need to tell SystemJS where to find it so we amend systemjs.config.js and include it in our map array.

Now we'll be able to import the Phoenix class from it wherever we need to. We'll need it in the service that we're going to use to wrap the Phoenix channel support. Lets take a look…

We import the library using the map name that we set up in our systemjs config to make it available to our class.  We then copy the code that Phoenix shows on the channels manual page to actually handle the connection.

This gives us a channel service that we'll be able to inject into components.

Angular 2 uses constructor injection so we'll be passing a channel service into the constructor function. Before we do that though we need to let the component know that our channel service is a provider so that we can properly typecast the injected variable. Once all this is saved run your app with 'npm start' and if you pop over to your browser window you should see error messages in your console log saying that the channel connection was refused, unless of course you're already running your Phoenix server.
Tip

06 September 2016

Laravel refusing to start up

I'm very much a fan of the clean implementation of Laravel but really dislike the fact that if there is something wrong with the .env file it refuses to give any meaningful information.

Laravel uses the vlucas/phpdotenv package to manage its environment.

It's pretty well known that if you have a space on either side the = sign in a key value pair then the .env file is invalid, but I had checked for this (and checked again).

Laravel will try to use its standard logging methods before they have actually had a chance to be booted up with the result that you're left with a "reflection error" exception message on the CLI rather than the actual cause of the problem in the dotenv package.

Debugging this is not trivial and I resorted to using strace to try and determine exactly what was going on.  Don't do this at home kids!  The easier solution is at the end of the article.

I used the following command to generate a trace of the system calls being made by PHP while trying (and failing) to run artisan optimize.

 strace php artisan optimize &> /tmp/strace.txt  

That let me walk through the calls and eventually confirm that the first PHP exception was thrown in the package that deals with reading the environment file.

 access("/var/www/raffle.bhf.org.uk.new/vendor/vlucas/phpdotenv/src/Dotenv.php", F_OK) = 0  
 ... more lines loading up more of the package and showing us processing it ....  
 access("/var/www/raffle.bhf.org.uk.new/vendor/vlucas/phpdotenv/src/Exception/InvalidFileException.php", F_OK) = 0  

But sadly there was no indication of exactly what the problem is with the file!

I decided that creating a minimal project dedicated to debugging my .env file was going to be faster than anything else.

I created a temporary directory and ran "composer require vlucas/phpdotenv".  Then I placed my faulty .env file into the directory and ran the following PHP file:

 <?php  
 require('vendor/autoload.php');  
 $dotenv = new Dotenv\Dotenv(__DIR__);  
 $dotenv->load();  

This gave me the actual exception in DotEnv which was that "Dotenv values containing spaces must be surrounded by quotes".  So it wasn't a space around the = sign but rather a space in one of my values, which made my life a lot easier!  As an extra bonus the first line in the stack showed exactly which key was problematic.
Tip

21 August 2016

Solving a Docker in VirtualBox DNS issue

I've recently been playing with Docker on Windows in conjunction with Linux on Windows.  I'm really amazed at how cool the stuff coming out of Microsoft is under Satya Nadella.  

When I was using Docker Toolbox on Windows my Dockerfiles would build correctly but as soon as I tried to run them in a Virtualbox host they would fail with the error "Something wicked happened resolving 'archive.ubuntu.com:http' (-5 - No address associated with hostname)"


Of course this error wasn't distribution specific and none of the distros I tried were working.

The stack that I am using is Windows Home hosting Ubuntu on VirtualBox which is running Docker.  I'm using bash on Linux for Windows because it's easier to do stuff like ssh but it's not relevant to this setup.

I tried setting the DNS in the Docker setup by using RUN steps to update /etc/resolv.  This felt a bit hacky and didn't work anyway.

In the end the fix was to go to my Windows shell and run ipconfig to get its DNS servers.  I then went and edited /etc/default/docker in my VirtualBox guest server and set the Docker options to use this DNS.  After restarting the docker service it was able to resolve DNS properly.

# Use DOCKER_OPTS to modify the daemon startup options.
DOCKER_OPTS="--dns 192.168.1.254"

I had installed Docker with the package but I think if you install with binary you would set the DNS when you start up the daemon.

Ironically Docker was easier to use with the Windows toolkit but I'll be deploying to Linux machines so I wanted to get this working under Ubuntu.
Tip

12 July 2016

Limiting how often you run seeds and migrations in Laravel 5.2 testing

Image: Pixabay
If integration tests take too long to run then we stop running them because they're just an annoyance.  This is obviously not ideal and so getting my tests to run quickly is important.

I'm busy writing a test suite for Laravel and wanted more flexibility than the built-in options that Laravel offers for working with databases between tests.

Using the DataMigrations trait meant that my seed data would be lost after every test.  Migrating and seeding my entire database for every test in my suite is very time consuming.

Even when using an in-memory sqlite database doing a complete migration and seed was taking several seconds on my dev box.

If I used the DataTransactions trait then my migrations and seeds would never run and so my tests would fail because the fixture data is missing.

My solution was to use a static variable in the base TestCase class that Laravel supplies.  It has to be static so that it retains its values between tests by the way.  This variable is a boolean flag and tracks whether we need to run the migrations and seeds.  We initialise the class with it set on and so the migrations and seeds will run the first time that any test run.

Now the fact that I'm using Laravel DataTransactions should spare me from affecting the database between tests but if I wanted to be 100% certain I could set the flag and have my database refreshed when the next test runs.

This also means that if the DataTransactions trait is not sufficient (for example I'm using multiple database connections) then I can manually refresh when I want to.


Tip

06 July 2016

Implementing a very simple "all of the above" checkbox

Image:Pixabay

Implementing a checkbox that lets the user "select all of the above" is trivial with jQuery.

The idea is that when the user selects the "all" checkbox then all of the other checkboxes are set to the same value. Similarly if they deselect any of the other options we need to turn off the "all" checkbox.

Tip

28 June 2016

Why am I so late on the Bitcoin train?

Image: Pixabay
I've been somewhat of a Bitcoin sceptic for quite some time.  When it first became a thing I was worried that governments would legislate it out of existence.

It has had a pretty bad rap of being associated with the dark web and it is definitely the choice of currency for malware authors.

In its normal usage Bitcoin is more transparent than cash.  If I give you a cash note there is no permanent record of the transaction and the tax man can't get a sniff into our business.

Governments hate transactions they can't tax or police and so in the beginning there was a concern that Bitcoin would be outlawed.

In contrast to cash, if I transfer you Bitcoin then there is a record of the transaction that anybody in the world can inspect.  It's possible to trace the coins in your Bitcoin wallet back through the various people who owned them.  Anybody in the world can watch the contents of your wallet and see where you spend your money.

This is exactly the sort of thing that governments love.

Of course not everybody wants to share their transactions with the world and so there are Bitcoin laundering services that attempt to anonymise the coins in your wallet.  This puts us back to square one with Bitcoin being very convenient for criminals to use in order to evade financial intelligence controls.

I suspect that the process of banning Bitcoin transactions would impinge too much on the freedom of citizens.  Some governments are talking about banning cryptography in order to maintain surveillance on their citizens so it's not a stretch to imagine them being displeased with Bitcoin laundering services.  *sigh*.

Anyway, back to my killer app for Bitcoin... I've recently emigrated and am still paying debt in South Africa.  Sending money back to South Africa costs about £30 and takes 2-4 days if I use the banking system.  If I use Bitcoin the process costs around £ 3 and I can have the money in my South African bank account on the very same day.

Bitcoin costs one tenth the price of using banks and is at least twice as fast.

My transaction is not at all anonymous and a government can trace the funds to me on either end of the transaction where copies of my passport are stored with the exchanges.  If I wanted to hide this from thieves, the government, spear-phishers, and other people who want to take my money without giving anything in return then I would use a Bitcoin laundry service.
Tip