Handling Arrays In PHP

arrays in phpToday, I’ll try to show you how to take advantage of the powerful built-in functions to handle arrays in PHP.

If you are a software developer, you will be used to handle data arrays in any language, so you’ll probably appreciate any given buil-in function that does all the dirty job for you.

This is not intended to be another tutorial of how to use arrays in PHP. I’d like to show you how combine the usage of the built-in functions, and how to create your own callable functions to create your custom filters, sorts or whatever you need.

If you have a look at php.net, PHP provide a lot of built-in functions, that allow you to transform, merge and/or split the data, accoding to your needs. Let’s start having a look at some of them individually, in order to see how to combine them later.


array_unique: Remove the duplicated elements from the array.

$a = array(0, 1, 1, 2);
array_unique($a); // array(0,1,2);

array_diff: Compare two arrays and return the difference respect from the first one.
$a = array(0, 1, 2);
$b = array(0, 2);
array_diff($a, $b); // array(1)

array_filter: Allow to apply a custom filter to the array. By default filters all elements that cast to boolean false.
$a = array(0 => true, 1 => false, 2 => 454, 3 => '');
array_filter($a); //array(0 => true, 2 => 454) Note that the key values are preserved

array_flip: Exchange the keys with the values.
$a = array("key" => "value");
array_flip($a); // array("value" => "key")

array_merge: Join all the arrays into a big one.
$a = array(1, 2, 3);
$b = array(3, 4, 5, 6);
array_merge($a, $b); //array(1, 2, 3, 3, 4, 5, 6) Note that duplicated elements are kept.

array_count_values: Show the number of ocurrences found for each value.
$a = array(1, 2, 1, 3, 2, 1);
array_count_values($a); //array(1 => 3, 2 => 2, 3 => 1);

array_map: Apply the given function to all the elements of the array. Check array_walk as well.
$a = array(" the", " developer ", " world ", " is ", " yours");
array_map("trim", $a); //array("the", "developer", "word", "is", "yours"); Note that the original array remains unchanged.

There are too many functions and I can’t show all of them here, because that would be another whole post. However you can always check the official page for more information and examples.

Now let’s see a couple of usages that can be useful many times.

For instance, imagine you are given an array of data, and first you want to do is clear it, and remove the empty values.

$data = array(" THE ", "developer ", "World", "", "IS", " yours ");
array_map('strtolower', array_filter(array_map('trim', $data)));
array (
  "the",
  "developer",
  "world",
  "is",
  "yours",
)

Firstly, trim all the elements, then filter the empty values, and finally transform all the data to lower case.
Another way of doing the same thing with an anonymous function:

array_filter(array_map(function($value) { return strtolower(trim($value));}, $data));

You might also try array_walk:

//Note that the following line will actually modify the original array
//We might use an anonymous function as well like in the previous example, now we use create_function
array_walk($data, create_function('&$val', '$val = strtolower(trim($val));'));
//Now we just need to clear out the empty values
array_filter($data);

Imagine we are working within a class, we want to apply a class method funcion to each array element, and we need to pass some extra variable as well.

class TheDeveloperWorldIsYours {
    public function parseData(&$data) {
        $prefix = 'Parsed: ';
        return array_walk($data, array($this, 'parseElement'), $prefix);
    }
    /**
     * parseElement
     * Prepend prefix to the given element if the key is even
     *
     * @param mixed $elem
     * @param mixed $key
     * @param mixed $prefix
     * @access protected
     * @return void
     */
    private function parseElement(&$elem, $key, $prefix) {
        $elem = $key % 2 == 0 ? $prefix . $elem : $elem;
    }
}

$data = array(1, 2, 3, 4, 5);
$a = new TheDeveloperWorldIsYours();
$a->parseData($data);
//Output:
array (
  0 => 'Parsed: 1',
  1 => 2,
  2 => 'Parsed: 3',
  3 => 4,
  4 => 'Parsed: 5',
)

Note that array_walk will call the given function with both the value and the key, in this order, and all the additional parameters will be placed afterwards.

Now, Imagine you have an array of data and you want to get only the duplicated elements.

$data = array('handling', 'arrays', 'in', 'php', 'handling', 'php', 'hi', 'php');
array_diff_key($data, array_unique($data));
array (
  "handling",
  "php",
  "php",
)

Note that we are comparing the array keys, because as we said previously, array_unique preserve them. So if we compare the original array with the unique, the repeated values (nor its keys) will not be present in the unique array, and we will get all the repeated elements as a result.
Imagine we just wanted to get once the repeated values. We might just apply an array_unique to the previous code, but we might do the following as well:

array_keys(array_filter(array_count_values($data), function($val) { return $val !=1;}));
array (
  'handling',
  'php',
)

And that’s about it. Now, each time you have to do some array filtering, sorting, transforming or whatever, firstly have a look at the php.net and think how you could do it without creating a loop :).

CSV File Validation In PHP (Part III)

This is the third and last part o the explanation of how to implement a csv file validation in php, using the Strategy Pattern.

If you remember the first post, we started implementing simple validations for each column, and in the second post, we wrote the main class that will take care of processing the whole CSV file.

Now, it’s time to implement the missing piece, to merge both parts. Basically, we need a class that should act as interface between our analyzer and the validators. We should apply a different validation’s implementation depending on the csv field.

In order to do that, I turn the field’s name into the class name that should take care of the validation of the field. In example, if the field is called “short_description”, then its validator class should be ShortDescription (UpperCamelCase). In order to separate the validators by phases, I prepend the phase with an underscore, so for instance, the previous example would eventually be Lexical_ShortDescription and Semantic_ShortDescription.

Then, all I have to do is instantiate the class, and call its method “validate” with the given input, and return the result.

This how it looks like:

class ValidatorContext {

private $strategy;
private $warnings;

/*
* Create the instance of the validator by building the class name with the UpperCamelCase format
* Generate a warning if the validator wasn't found, and create a generic validator instance
*
* @param $type string type of analysis
* @param $strategy string validator strategy class name
* @param $optData array optional data passed to the semantic validators
* @return void
*/
function __construct($type, $strategy, $profile, $optData = null) {
$this->warnings = array();
$validator = ucfirst($type) . '_' . str_replace(' ', '', ucwords(str_replace('_', ' ', $validator)));
if(class_exists($validator)) {
$this->strategy = new $validator($profile, $optData);
}else {
$this->strategy = new Generic_Validator();
$this->warnings[] = sprintf('Strategy %s (%s) is not implemented for the %s analizer ' . "\n", $strategy, $validator, $type);
}
}

/**
* Remove elements from memory
* @return void
*/
function __destruct() {
unset($this->strategy);
}

/**
* getTokens
* Return the tokens retrieved from the validator
* @return array
*/
public function getTokens() {
return $this->strategy->getTokens();
}

/**
* getErrors
* Return the errrors found during the validation
* @return array
*/
public function getErrors() {
return $this->strategy->getErrors();
}

/**
* getWarnings
* Return the warnings found during the validation
* @return array
*/
public function getWarnings() {
return $this->warnings;
}

/**
* getErrorMsg
     * Return the errror description to help the user to solve the problem
* @param $input string|array
* @return string
*/
public function getErrorMsg($input) {
return $this->strategy->getErrorMsg($input);
}

/**
* validate
* @access public
* @param $input mixed input data of the csv
* @return bool whether this data is valid or not for current validator
*/
public function validate(&$input) {
//Uncomment the line below to debug
//echo get_class($this->_strategy) . ": " . $input . "\n";
return $this->strategy->validate($input);
}

}

And basically, that’s about it. You might thrown an exception if there is no validation found for a certain field, but in my case its enough to just bypass the validation, so the Generic_Validator class, will always return true.

I hope this can help you to implement your own csv validation in php. If you want to download the source code of the whole application, I’ll put the link with some working examples of usage included during this week.

Updated on 14/04/2013

I’ve created a git repository with the source code of the CSV validator. I’ve included an example of usage with some demo files as well. All you have to do is clone the repository and execute “example.php” from php-cli.

You get the source code of the validator from here.