php[architect] November 2018

(singke) #1
http://www.phparch.com \ November 2018 \ 5

The Case for Generics in PHP

I could also make a DuckArrayCollection (Listing 5).
Every time I wish to have an ArrayCollection of homog-
enous items, I have to make a new ArrayCollection class to
enforce this contract. This would allow me to write the code
in Listing 6.


Generics to the Rescue


My ultimate wish would be to signal that the method would
return “an ArrayCollection of User objects” without creating
a new child class for every type of object I might return. It’s
an ArrayCollection whose members are only allowed to be
instances of the User class. Here’s a possible evolution of the
previous example.


public class UserLookupService
{
public function getUsersByDepartmentName(
string $departmentName ) : ArrayCollection[User]
{
return $this->userRepo
->getByDepartment($departmentName);
}
}


What I wish I could do is this:

class UserLookupService
{
// ...
public function getUsersByDepartmentName(
string $departmentName) : User[] //<-- Not valid
{
return $this->userRepo
->getByDepartment($departmentName);
}
}


The brackets syntax is common in many languages to desig-
nate arrays, but this isn’t what we’re working with here. In the
end, what we are trying to define is a composite type. It’s an
object of a given type—ArrayCollection—made of objects of
another type—User.


The Generics RFC’s proposed syntax for this would be:

ArrayCollection


Applying Generics


The collections examples look tedious. Generics would
allow us to remove this tedium, as in Listing 7.


The letter T acts as a placeholder for whichever type I need
to bind my GenericArrayCollection, and this binding happens
at instantiation.


$duckCollection = new GenericArrayCollection();


From here on, the add method will only accept a Duck. And
the next method is guaranteed to only ever return an instance
of Duck.


Based on the above examples, from this single
GenericArrayCollection class, I can now write Listing 8.
With this generics syntax, our UserLookupService class
could now look like Listing 9.
With the above example, we are now guaranteeing the
method would always return a collection of User objects.

Listing 5


  1. <?php



  2. class DuckArrayCollection extends ArrayCollection

  3. {

  4. public function addDuck(Duck $duck) {

  5. parent::add($duck);

  6. }



  7. public function nextDuck(): Duck {

  8. return parent::next();

  9. }

  10. }


Listing 6


  1. // type-specific collection class for "User"

  2. $users = new UserArrayCollection();



  3. // this is fine

  4. $users->addUser(new User());



  5. // this breaks, as intended

  6. $users->addUser(new Duck());



  7. // type-specific collection class for "Duck"

  8. $ducks = new DuckArrayCollection();



  9. // this is fine

  10. $ducks->addDuck(new Duck());



  11. // this breaks, as intended

  12. $ducks->addDuck(new User());


Listing 7


  1. <?php




  2. //don't extend ArrayCollection, re-implement from scratch



  3. class GenericArrayCollection

  4. {

  5. public function add(T $element) {

  6. $this->elements[] = $element;

  7. return true;

  8. }



  9. public function next(): T {

  10. return next($this->elements);

  11. }

  12. }

Free download pdf