PHP: Adding Behavior to a URL Class

In this second part of a series, I add a basic method to the “Url” class created in the first part. This method will be tasked with appending new parameters to its existing query string. Due to the immutable nature of the class, the method will return new “url” objects, in this way demonstrating the actual functionality of the Value Object pattern when it comes to taking immutability to a more complex level.

While it’s fair to say that it’s not one of the most popular design patterns, like Factory, Singleton and Decorator, Value Object is a simple, yet powerful pattern. It can be used for fabricate value objects, or expressed in other words, objects whose equality is based on the values assigned to their fields, and not on an identity.

Classic examples of value objects that are often modeled in modern applications are dates, email addresses, phone numbers and so forth. Naturally, it’s possible to have multiple types of value objects, other than the ones I just mentioned, depending on how they behave within the context of a given domain. Also,  despite their rather intimidating name, value objects are dead simple to model, as in most cases they’re simple data containers whose properties are populated according to predefined constraints. Consider the case of an email address, for example; its parts must be compliant with the specifications of RFC 5321 and RFC 5322.

In the case of PHP, however, creating value objects can be a challenging task, especially when it’s necessary to make them immutable. Unlike other more mature languages like Java or C#, PHP doesn’t support natively immutable value objects. However, the process is fairly straightforward, and most importantly, it can be mastered in a snap.

To demonstrate how easy it is to achieve this, in the introductory installment of this series I started building a trivial class, which was responsible for modeling immutable URL objects. Although in its current state the class effectively prohibits the assignment of new values to its fields once they’ve been populated through the constructor, its behavior is limited to housing those values and exposing them to client code via a bunch of getters. Sad but true.

Considering this situation, it’d be useful to provide this sample class with some kind of simple behavior, so you can see how it reacts against any attempt to modify its properties. In consonance with this idea, in this second part of the series I’m going to add to the URL class a method that will append a few additional parameters to its existing query string.

The question that comes up here is: will this method break the class’s immutability? Well, the answer is resounding no! But, if you wish to learn how this will be accomplished, keep reading.

Creating immutable value objects in PHP: a quick look at an earlier example

Since my goal is to show how to preserve the immutability of the URL class developed in the previous tutorial after expanding its core functionality, it’d be useful to take a quick look at the class, so that you can recall how it was defined initially. 

With that said, here’s the source code of this sample class. As I said, it is responsible for modeling simple and immutable URL objects:

(Url.php)

<?php

final class Url
{
    private $_scheme; 
    private $_host;
    private $_path;
    private $_queryString;
   
    // constructor
    public function __construct($url)
    {
        if (!filter_var($url, FILTER_VALIDATE_URL, array(FILTER_FLAG_SCHEME_REQUIRED, FILTER_FLAG_HOST_REQUIRED))) {
            throw new UrlException(‘The specified URL is invalid.’);
        }
       
        $url = parse_url($url);
        $this->_scheme = $url['scheme'];
        $this->_host = $url['host'];
       
        if (isset($url['path'])) {
            $this->_path = $url['path'];
        }
       
        if (isset($url['query'])) {
            $this->_queryString = $url['query'];
        }
    }
   
    // get the scheme part of the URL
    public function getScheme()
    {
        return $this->_scheme;
    }
   
    // get the host part of the URL
    public function getHost()
    {
        return $this->_host;
    }
   
    // get the path part of the URL
    public function getPath()
    {
        return $this->_path;
    }
   
    // get the query string of the URL
    public function getQueryString()
    {
        return $this->_queryString;
    }
}     

 

(UrlException.php)

<?php

class UrlException extends Exception{}

As you can see from the above code fragment, the “Url” class bases its immutability on the deliberate absence of setter methods. Of course, there are a few other approaches you can use for achieving a similar result, but I used this one because I found it easier to implement and read.

Other than that, there’s not much more that can be said about this class, since at this moment it only takes a URL string through its constructor and then dissects each part, which is in turn assigned to a different field. Although this example does show that creating immutable value objects in PHP is a pretty straightforward process, it doesn’t demonstrate the full potential of the Value Object pattern. Consider, for instance, the situation discussed earlier, which introduces the possibility of adding some kind of behavior to the class, such as appending extra arguments to the existing query string.

Well, in a case like this, the task should be performed by a discrete method, which for obvious reasons should keep the immutability of the class untouched. At a glance, it seems like this is a complex thing to achieve, right? Fear not, as coding such a method is much simpler that you might think, trust me.

To demonstrate this, in the following segment I’m going to extend the functionality of the previous “Url” class by adding to it the method just mentioned. As usual, to learn the full details of this process, leap forward and read the lines to come.

{mospagebreak title=Extending the immutability of the Url class by adding query string parameters}

To be frank, the full functionality of the Value Object pattern is leveraged when the implementation of a method tries to modify in some way the values assigned to an immutable class. To elaborate on this concept, below is the implementation of a brand new method corresponding to the earlier “Url” class. It adds some arguments (or attempts to do so) to its $_queryString field. Check it out:

// add new query string parameters (returns a new Url object)
public function addQueryString(array $params)
{
    if (empty($params)) {
        throw new UrlException(‘The specified query string parameters are invalid.’);
    }
   
    $queryString = ”;
    foreach ($params as $key => $value) {
        $queryString .= ‘&’ . $key . ‘=’ . urlencode($value);
    }
    $queryString = $this->_queryString === null ?
                 trim($queryString, ‘&’) :
                 $this->_queryString . $queryString;
    $url = $this->_scheme . ‘://’ . $this->_host . $this->_path . ‘?’ . $queryString;
    return new Url($url);
}

That looks pretty interesting, doesn’t it? As you can see, the above “addQueryString()” method takes an associative array as an incoming argument, and each key/value pair is added to the query string of the class. While this process is pretty self-explanatory, the most relevant aspect of the method it that instead of modifying the existing query string, it creates a new one which is used to also create a new URL, which in turn is utilized to spawn a new “Url” object.

This shows the nature of an immutable value object: yes, any attempt to alter the values assigned to its existing fields will result in the creation of a new value object of the same type, thus properly preserving its immutability.

And now that you’ve surely grasped the logic of the “addQueryString()” function, here’s the finished version of the “Url” class, after including the function into its source code:

(Url.php)

<?php

final class Url
{
    private $_scheme; 
    private $_host;
    private $_path;
    private $_queryString;
   
    // constructor
    public function __construct($url)
    {
        if (!filter_var($url, FILTER_VALIDATE_URL, array(FILTER_FLAG_SCHEME_REQUIRED, FILTER_FLAG_HOST_REQUIRED))) {
            throw new UrlException(‘The specified URL is invalid.’);
        }
       
        $url = parse_url($url);
        $this->_scheme = $url['scheme'];
        $this->_host = $url['host'];
       
        if (isset($url['path'])) {
            $this->_path = $url['path'];
        }
       
        if (isset($url['query'])) {
            $this->_queryString = $url['query'];
        }
    }
   
    // get the scheme part of the URL
    public function getScheme()
    {
        return $this->_scheme;
    }
   
    // get the host part of the URL
    public function getHost()
    {
        return $this->_host;
    }
   
    // get the path part of the URL
    public function getPath()
    {
        return $this->_path;
    }
   
    // get the query string of the URL
    public function getQueryString()
    {
        return $this->_queryString;
    }
   
    // add new query string parameters (returns a new Url object)
    public function addQueryString(array $params)
    {
        if (empty($params)) {
            throw new UrlException(‘The specified query string parameters are invalid.’);
        }
       
        $queryString = ”;
        foreach ($params as $key => $value) {
            $queryString .= ‘&’ . $key . ‘=’ . urlencode($value);
        }
        $queryString = $this->_queryString === null ?
                     trim($queryString, ‘&’) :
                     $this->_queryString . $queryString;
        $url = $this->_scheme . ‘://’ . $this->_host . $this->_path . ‘?’ . $queryString;
        return new Url($url);
    }                
}

Done. At this point, the “Url” class is complete and ready to be tested. Therefore, in the next section I’m going to set up a simple example, which will demonstrate how to create a couple of value objects using the class.

Of course, if you’re like me it’s probable that you’re wondering if those objects will be really immutable. Well, the only way to get your question answered is by looking at the example. So hurry up and read the following section.   

Building a final example: putting the Value Object pattern into action

As I promised in the section that you just read, I’ve included a minimalist example that shows the immutable nature of the previous “Url” class. Take a close look at it, please:

<?php

require_once ‘Url.php';

// create a new Url object
$url1 = new Url(‘http://www.devshed.com’);

// append some query string params to the previous URL (returns a new Url object)
$url2 = $url1->addQueryString(array(‘userid’ => ‘1’, ‘name’ => ‘Alex’));

if ($url1 == $url2) {
    echo ‘The two value URL objects are equal.';
}
else {
    echo ‘The two value URL objects are not equal.';
}

/*
displays the following
The two value URL objects are not equal.
*/

In this case, the above script starts creating a new value object, which is properly populated with a well-formed URL; then, a second object is generated by calling the “addQueryString()” method (remember that this one is basically a factory).

With these two objects living comfortably side by side, a trivial comparison (note the use of the “==” operator) is made in order to determine if the objects in question are equal, not identical. As one might expect, the result of this process is false, which demonstrates the immutability of the involved objects.

Needless to say, it’s possible to add more methods to the “Url” class which try to modify the initial values assigned to its properties, similar to the “addQueryString()” function that you learned before. In all cases, these methods should return new URL objects, regardless of the form of processing applied to the pertinent fields.
 
Final thoughts

In this second part of the series, I added to the previous “Url” class a basic method which was tasked with appending new parameters to its existing query string. Due to the immutable nature of the class, the method returned new “Url” objects, thus demonstrating the actual functionality of the Value Object pattern when it comes to taking immutability to a more complex level.

With this method properly implemented, it was easy to illustrate how to use it for creating new value objects upon its invocation. Moreover, this process showed a key concept of a value object: any type of additional processing applied to the object will result in the creation of a new one, usually via a factory method. Do you understand? I bet you do.

Naturally, it’s possible to model value objects that are different from the ones that you saw in the previous example. In line with this idea, in the next part I’m going to show how to apply the Value Object pattern to a class responsible for creating simple HTML widgets.

Don’t miss the upcoming tutorial!

[gp-comments width="770" linklove="off" ]
antalya escort bayan antalya escort bayan