PHP, Programming

PHP Type Hinting Arrays Using The Splat Operator

It’s possible to type hint an array (sort of) in PHP without using a Doc block.

Version 5.6 added a splat operator or sometimes called argument unpacking. A splat operator is 3 dots before a parameter. The splat operator allows a user to pass in an arbitrary amount of parameters. The arbitrary parameters are then turned into an array by PHP.

For example if we have a method that adds items to a cart and expects each item added to the cart is an instance of CartItem we can do the following:

function addItemsToCart(CartItem ...$cartItems) {
    //$cartItems is an array of CartItem objects
}

$cartItem1 = new CartItem();
$cartItem2 = new CartItem();
$cartItem3 = new CartItem();

addItemsToCart($cartItem1, $cartItem2, $cartItem3);

The function can now be called using 1 or many CartItem parameters. The end result is $cartItems contains an array of CartItem objects.

Notice how we just pass as many parameters as we want to the function and the splat operator handles the rest. We could pass in 5 or 100 and the result would still be an array of CartItem objects. The splat operator allows someone to pass in an arbitrary amount of parameters to a function turning those parameters into an array.

Pros

  • IDEs can identify the type hint which helps auto completion and preventing simple mistakes.
  • Other developers reading the code can quickly identify what type is expected to be passed in.
  • PHP will throw an error if someone tries to pass something else besides a CartItem object.

Cons (maybe)

  • When using splat operator the splat parameter must be last. This is because it allows an arbitrary amount of parameters.
  • Other developers may not be familiar with the splat operator, but a quick look at PHP documentation may help.

Using array_reduce to Transform Data

First, why we might transform data? If we have raw data from a database and need to send data to an external system or maybe export data. In either case we most likely don’t want to expose database column names or the structure returned must be different than how the data is stored.

For our example we’ll be sending data from our database to a CRM. Here’s the example class. In the database we probably store first and last name separately, but the CRM expects that we pass in the full name. Likewise the CRM expects a full address and company name.

class TransformUserForCrm {
    private $columns = [
        'fullName',
        'company',
        'address',
    ];

    public function prepareData($rows)
    {
        $data = [];
        foreach($rows as $row) {
            $data[] = array_reduce($this->columns, function ($result, $column) use ($row) {
                $methodName = 'get' . ucfirst($column);
                $result[$column] = (method_exists($this, $methodName)) ? $this->$methodName($row) : $row->$column;
                return $result;
            }, []);
        };
        return $data;
    }

    private function getFullName ($row) {
        return $row->firstName . ' ' . $row->lastName;
    }

    private function getAddress ($row) {
        return $row->street . ' ' . $row->city . ', ' . $row->state . ' ' . $row->postalCode;
    }
}

The $columns array defines the output column names.

In prepareData we foreach through each of the $rows.  Each row calls array_reduce. Simply put array_reduce will reduce an array to a single value by way of a callback function. This means we can call array_reduce on each row of data to transform the data into another array with the proper structure and formatting.

Blank array_reduce function with no logic and blank array passed in for initial value.

$data[] = array_reduce($arrayToReduce, function ($result, $valueFromArrayToReduce) {
    //Logic for each iteration goes here
}, []);

array_reduce takes 3 parameters.

  • First parameter ($arrayToReduce) is the array to reduce to a single value.
  • Second parameter is the callback function that is called for each element of $arrayToReduce. The callback itself has two parameters: previous value returned by the callback function ($result) and current iteration value of $arrayToReduce which we have named $valueFromArrayToReduce.
  • Third parameter is the initial value to pass into $result as the previous value because there is no previous value on the first iteration.

The class above implementation of using array_reduce below:

$data[] = array_reduce($this->columns, function ($result, $column) use ($row) {
    $methodName = 'get' . ucfirst($column);
    $result[$column] = (method_exists($this, $methodName)) ? $this->$methodName($row) : $row->$column;
    return $result;
}, []);

The final output structure we want is from $this->columns so that goes into the first parameter.

The callback function carries the result from each previous callback interation unless it’s the first time running through in which case it passes [] because the final parameter of array_reduce is []. Second parameter is the current column we are working on.

In the callback check to see if a method exists on the class. This defines a standard for retrieving data. For example the fullName column will call getFullName method. If a method is not defined then it will assume the value is fine the way it is and puts the raw value in for output.

Each iteration of the callback adds the column to the result by $result[$column]. The result is an array that we keep adding columns to until array_reduce is done. When array_reduce is done the result is returned and added to the $data array. Eventually $data contains all transformed rows.

This method of holding all data in memory doesn’t work well for massive datasets, but works well for small to medium size datasets.

Example

Using the class above given the following data:

$user1 = new stdClass();
$user1->firstName = 'Nick';
$user1->lastName = 'Escobedo';
$user1->street = '123 Fake St.';
$user1->city = 'Chicago';
$user1->state = 'IL';
$user1->postalCode = 12345;
$user1->company = 'Fake Company';

$user2 = new stdClass();
$user2->firstName = 'Will';
$user2->lastName = 'Smith';
$user2->street = '456 Fake St.';
$user2->city = 'Chicago';
$user2->state = 'IL';
$user2->postalCode = 56789;
$user2->company = 'Fake Company';

$data = [$user1, $user2];

$transformer = new TransformUserForCrm();

print_r($transformer->prepareData($data));

Output:

array (
    array (
        'fullName' => 'Nick Escobedo',
        'company' => 'Fake Company',
        'address' => '123 Fake St. Chicago, IL 12345',
    ),
    array (
        'fullName' => 'Will Smith', 
        'company' => 'Fake Company', 
        'address' => '456 Fake St. Chicago, IL 56789'
    ),
)

How To Bind An Interface To An Implementation In Laravel

Binding an interface to an implementation promotes good coding practices. As a result the code is less coupled, more maintainable, and testable.

Why might someone want to bind an interface using Laravel? To put an abstraction between the application and the concretion. A concretion is a class that implements the interface. It is a specific implementation, in our example it will be Amazon S3 file storage provider. The application doesn’t care which implementation it receives just that it receives an implementation with the guaranteed functions.

Lets build functionality to interact with files on a storage provider such as Amazon S3. Initially all files will be uploaded and deleted on S3 only. There are other ways to accomplish this and the main goal is to demonstrate how to bind an interface to an implementation and not so much the actual code to upload to a storage provider.

Create An Interface

The interface will determine which functions are available on the concrete implementation through the binding.

Create The Concrete Implementation

The concretion is an Amazon S3 file storage provider. This file is where Amazon specific upload/delete functionality goes.

Create The Laravel Service Provider

The Laravel service provider is the mechanism that binds the FileStorageInterface to the S3FileStorage class implementation. This means if we use dependency injection for the FileStorageInterface or use App:make it will automatically resolve to the S3FileStorage class.

Using The Interface

In the below example we are not using the implementation directly. Instead it is automatically resolved through the service provider which returns the concrete implementation. Laravel will automatically resolve dependencies if they are in the __construct() functions. We can see below that the controller doesn’t know about Amazon and nor should it. I am only using the controller in this example to provide a demonstration. There are other places where we would use the storage provider that might better organize the code.

Final Thoughts

If we wanted to swap out for Dropbox or some other provider the impact is minimal, create a Dropbox implementation and change the provider. The application would now use the Dropbox implementation instead of the Amazon S3 implementation.

Following this pattern can also help separate the application from Laravel which could make reusing this code in other projects easier than if the Amazon S3 code were included in a model directly. Definitely think about how to best structure your code and not to fall into the framework convenience trap of putting everything in a model, controller or view. In most cases there are better places for code than those 3 areas.

Laravel 4 Project On Shared Hosting

Laravel 4 is capable of running on a shared hosting environment. It’s a lot easier than you probably think.

  1. Login to your host and go to your home directory. This does not mean your public home directory, they are separate.
  2. Upload all the contents Laravel project EXCEPT the public folder to your home directory.
  3. Upload the contents of your Laravel public directory to your public directory on your host. The public directory is sometimes called public_html.
  4. Edit file paths.php located in the bootstrap folder so the public path matches the path to your public directory on your host.

After completing each of those steps you should now have a working Laravel 4 project on a shared hosting environment. This assumes the shared host has all the PHPmodules required to run Laravel as well. I was able to accomplish this on HostGator.

If you have any questions feel free to ask.

Xampp Send Email Issues

After upgrading my xampp installation I had issues sending email.

I wrote a small test PHP page and it was saying my emails were being sent, but I never received them. Obviously something was misconfigured.

I thought I correctly edited my php.ini file, but it turns out all the tinkering I did with it may have caused the issues.

Search your php.ini file for [mail function].

  1. Remove the comment on smtp and smtp_port by removing the semi colons before them.
  2. Add smtp server, port number. I’m using my ISP smtp server. Find your ISP smtp settings by searching google.
  3. sendmail_from needs to be set only if you don’t specify a from header in your PHP code. This can be set to any email address.
; XAMPP: Comment out this if you want to work with an SMTP Server like Mercury
SMTP = examplesmtpserver.com
smtp_port = 25

; For Win32 only.
; http://php.net/sendmail-from
sendmail_from = email you want to send from (ex: [email protected]). This needs to be set or a from header must be added in the php mail code

 

Here is test PHP code that I used. You’ll have to edit the “to” variable to your email address.

$to = "your email here";
$subject = "Test Email";
$body = "Email for testing Xampp";

if (mail($to, $subject, $body)){
	echo("Message successfully sent!");
} 
else{
	echo("Message delivery failed…");
}

After visiting your created PHP page the page should tell you whether PHP sent your email. Please note that just because PHP says it sent the mail doesn’t actually mean the email will be delivered  While I was having issues PHP still said it successfully sent, but I wasn’t receiving emails. If you’re still having problems after it says email successfully sent you’ll have to do more digging.

Contact me if you have questions I’ll do my best to assist [email protected].

 

 

SoapClient Error after enabling FedEx Shipping Method on Magento Ecommerce Solution

In this tutorial I will explain how to fix the SoapClient error and why it happened. My server threw this error after enabling the FedEx shipping option in Magento running 1.7. I isolated the issue to the FedEx shipping option because every time I enabled FedEx the shopping cart. The next step was to check the Magento error logs which gave me the errors below.

2012-10-19T03:57:53+00:00 ERR (3): Warning: include(SoapClient.php): failed to open stream: No such file or directory  in /var/www/vhosts/www.domain.com/httpdocs/lib/Varien/Autoload.php on line 93
2012-10-19T03:57:53+00:00 ERR (3): Warning: include(SoapClient.php): failed to open stream: No such file or directory  in /var/www/vhosts/www.domain.com/httpdocs/lib/Varien/Autoload.php on line 93
2012-10-19T03:57:53+00:00 ERR (3): Warning: include(): Failed opening 'SoapClient.php' for inclusion (include_path='/var/www/vhosts/www.domain.com/httpdocs/app/code/local:/var/www/vhosts/www.domain.com/httpdocs/app/code/community:/var/www/vhosts/www.domain.com/httpdocs/app/code/core:/var/www/vhosts/www.domain.com/httpdocs/lib:.:')  in /var/www/vhosts/www.domain.com/httpdocs/lib/Varien/Autoload.php on line 93

First I’ll explain why it happened. The reason for my SoapClient error was the php-common and php-soap extensions were at a different version than my PHP installation itself.
PHP was at version 5.3.3 and php-common and php-soap were at 5.3.17. So it’s important to make sure that the PHP extension versions are compatible with the PHP version.

Secondly I’ll explain how to fix this issue. It was extremely easy.
*Note:  There is always the potential that an update may break your site make sure you have a backup in place and ready to go before completing this process!
I simply ran the following command. I knew the PHP version was old compared to the extensions so this updates the PHP installation.

yum update php

After updating the PHP restart Apache by running the following command.

service httpd restart

Refresh the website and verify the checkout works properly.

If you have questions feel free to email [email protected] or leave a comment below.

Build Email Message Dynamically From Submitted Form

I wrote some code to dynamically generate an email message based on the names of form elements being sent through post.
In this post I’ll describe how it’s done.

The foreach loop grabs the keys from the post array. An example key for this  $_POST[‘Name’] would be “Name”. It’s important to put in the name of the field how you want it to show in the email. So if you want uppercase letters or spaces put them in.

The $keywos (key without spaces) gets the current key in the foreach loop and replaces the underscores that are automatically put in if the name from the HTML form elements had spaces when posting and replaces them with spaces.

Copy the post array to values array

If the current value for a particular key doesn’t have information in it don’t add it to the email message. This means the user hasn’t typed in any information in the input or selected an option.

If the key isn’t equal to submit then add it to the email messages. The submit button doesn’t need to be added to the email message.

 

Have questions? Email me at [email protected].