Context
APIs are often designed to operate with scalars. As described in the following example, the function addContact() operates on a single element:
<?php
class User {
protected $contacts = array();
function addContact($contact) {
$this->contacts[] = $contact;
}
}
?>
In a context where many contacts have to be added using the above API, code usually looks like:
<?php
// Some contacts
$contacts = array("Paul", "John", "Maria");
$user = new User();
// Looping over contacts to add them
foreach ($contacts as $contact) {
$user->addContact($contact);
}
?>
Inner looping
A way to avoid repeating this looping everywhere would be to design the API to work with array of contacts:
<?php
class User {
protected $contacts = array();
function addContacts($contacts) {
foreach ($contacts as $contact) {
$this->contacts[] = $contact;
}
}
}
?>
This might make the code, where loops are in use, somewhat clearer:
<?php
// Some contacts
$contacts = array("Paul", "John", "Maria");
$user = new User();
$user->addContacts($contacts);
?>
With the benefit of speeding the processing a little bit as only one function call is issued!
The side effect is that adding only one contact is not as elegant:
<?php
$contact = "Julia";
$user = new User();
$user->addContacts(array($contact));
?>
A nice PHP trick
The good news is that PHP provides a nice array cast operator: (array) which will transform a scalar value into an array. As described on the manual page about arrays:
"For any of the types: integer, float, string, boolean and resource, converting a value to an array results in an array with a single element with index zero and the value of the scalar which was converted. In other words, (array)$scalarValue is exactly the same as array($scalarValue)."
Previous example can be transformed to play nicely with both scalars and arrays:
<?php
class User {
protected $contacts = array();
function addContacts($contacts) {
foreach ((array) $contacts as $contact) {
$this->contacts[] = $contact;
}
}
}
?>
Function addContacts() can now be used the following way with scalars:
<?php
$contact = "Julia";
$user = new User();
$user->addContacts($contact);
?>
OOPs!
This is a nice trick to define APIs to be used with both scalars and arrays, however this will not work when using objects! PHP is able to cast an objects as an array, but this will give you an access to its properties which is not the intended purpose.
If your contacts are objects you will have to modify the addContacts() function to something like:
<?php
class User {
protected $contacts = array();
function addContacts($contacts) {
if (is_array($contacts)) {
foreach ($contacts as $contact) {
$this->contacts[] = $contact;
}
} else {
$this->contacts[] = $contacts;
}
}
}
?>
This may not be as elegant as the array casting method. However it will enable your API to work seemlessly with both scalars and arrays when objects are used.
The benefit of working with an array capable API is that you might sometimes optimize the operations. For example, in the case of retrieving or deleting elements from a database, you might want to use the "WHERE id IN (...)" syntax to match multiple elements rather than one by one. In the X examples that has been used in this article, an interesting optimization is to use the native array_merge() function which avoids reinventing the wheel by looping over elements using a foreach construct and adding elements one by one:
<?php
class User {
protected $contacts = array();
function addContacts($contacts) {
if (is_array($contacts)) {
$this->contacts = array_merge($this->contacts, $contacts);
} else {
$this->contacts[] = $contacts;
}
}
}
?>
Conclusion
In this article we have seen the advantages of creating an API which can handle multiple elements at once. If there is a benefit in terms of speed (which heavely depends on your business logic), don't forget that code readability is of higher importance too! Hopefully, this tip should improve both
For those who mind about performance, doing:
<?php
// Adding 5.000.000 contacts
$user->addContacts(range(1, 5e6));
?>
is about 40% faster than:
<?php
foreach (range(1, 5e6) as $contact) {
$user->addContact($contact);
}
?>
Thanks to Paul Dragoonis, Paul Borgermans and Jérôme Renard for reviewing this article

