Jump to content
Larry Ullman's Book Forums

Do You Create Object Collections In Php


Recommended Posts

Hey, everyone

 

How common is it develop some sort of collection in PHP? I do this a lot in Java. I create methods such as add(), remove(), get(), containts(), isEmpty(), size() and iterator(). This allow me to handle a group of objects as a single object. It will also allow me to treat the collection as an array, stack or queue if I want to. I really love this flexibility.

 

The client side code will often then look like this.

 

// Create contact list
$contact = new Contacts();

// Create a few person objects
$larry = new Person("Larry Ullman", "telephone number", "Other info");
$thomas = new Person("Thomas Larsson", "telephone number", "Other info");
$jon = new Person("Jon somename", "telephone number", "Other info");

// Add a few contacts
$contact->add($larry));
$contact->add($thomas);
$contact->add($jon);

// I don't need Jon anymore
$contact->remove($jon);

// Get an iterator to loop through contacts
$contacts = $contact->Iterator();

// Print all contacts
while ($contacts->hasNext())
{
  echo $contacts->next();
}

 

 

It's just that I haven't seen any kind of code like this in PHP before. Is this because of PHP's array capabilities, or what is it? Because you can treat PHP arrays like maps, lists and normal arrays, the NEED for collections is not really that big. The collection is pretty much a wrapper for the array to make your life easier.

 

I really need these kinds of collection in a large personal project, where I often has collection inside collection again. This is why I wanna know. :)

 

While creating this thread, I decided to search at stack overflow. It has been addressed here, but I want your view too.

 

http://stackoverflow.com/questions/1580223/java-like-collections-in-php

Link to comment
Share on other sites

Just wanted to update on this. I decided to go for developing my own Collection based on a normal array. I built an abstract superclass which should be extended by small sub classes. The Collection implements an interface Listable, with the sole method get_key( ) which is used to set the key in the array for our objects. The point is that Collection implements Listable WHILE having Listable object hinting for the methods. (This allows several collections to be the array elements of ONE collection.)

 

The collection will offer functionality such as add( $object ), remove( $anytype ), get( $key ), getAll( ), contains( $anytype ) and size( );.

 

The abstract collection looks like this:

 


<?php

interface Listable
{

/**
 * GET_ID()
 * Collections need a clear and unique ID to work
 */

public function get_key ( );

/**
 * __toString()
 * Must implement the magic method toString
 */

public function __toString ( );

}

<?php
abstract class Collection implements Listable
{

private $array = null;

public function __construct ( )
{
 $this->array = array();
}

public function add ( Listable $object )
{

 // Remove duplicates
 if ( $this->contains($object) )
 {
  return "Object already exists";
 }

 // Add to array
 $this->array[$object->get_key()] = $object;
}

public function add_event( Event $object)
{
 $key = count($this->array)+1;

 // Set key as object id
 $object->set_key($key);

 // add to array
 $this->array[$key] = $object;
}

public function remove ( $anytype )
{
 $key = $this->retrieveKey($anytype);

 unset($this->array[$key]);
}

/**
 * GET
 * Get an object from the array
 * @param Object The array key to return
 * @return Object The object if found, else null
 */

public function get ( $key )
{
 // Get The object
 return (isset($this->array[$key])) ? $this->array[$key] : null;
}

public function getAll ( )
{
 return $this->array;
}

public function size ( )
{
 return count($this->array);
}

/**
* CONTAINS
* Checks wheter the object is in the array
*
* @param anytype $anytype A key or an object
* @return boolean	true if not in array, else false
*/

public function contains ( $anytype )
{
 $key = $this->retrieveKey($anytype);

 return ($key != null) ? true : false;
}

/**
* RETRIEVE
* Retrieve object key
*
* @param anytype $anytype A key or an object
* @return boolean	key if in array, else null
*/

private function retrieveKey ( $anytype )
{
 // Check whether anytype is a Listable object
 if ( $anytype instanceof Listable  )
 {
  return (isset($this->array[$anytype->get_key()])) ? $anytype->get_key() : null;
 }
 // Use key directly
 return (isset($this->array[$anytype])) ? $anytype : null;

}
}

 

A typical subclass of Collection would look like this:

 

<?php

class Round extends Collection
{

private $id;
private $name;

public function __construct($id, $name)
{
	$this->id = $id;
	$this->name = $name;
}

public function add ( Match $club )
{
	parent::add($club);
}

public function get_key ( )
{
	return $this->id;
}

public function __toString()
{
	return $this->name;
}

}

?>

 

Or this:

 

<?php
include(&#39;event.php&#39;);
include(&#39;goal.php&#39;);
include(&#39;card.php&#39;);
class Match extends Collection implements Listable
{

private $id;
private $home_team;
private $away_team;
private $kickoff;
private $home_goals;
private $away_goals;
private $number_of_events = 0;

public function __construct($id, $home_team, $away_team, $kickoff, $goals_home = null, $goals_away = null)
{
 $this->id = $id;
 $this->home_team = $home_team;
 $this->away_team = $away_team;
 $this->kickoff = $kickoff;
 $this->home_goals = $goals_home;
 $this->away_goals = $goals_away;
}

public function add ( Event $event )
{
 parent::add_event($event);
}

public function get_key ( )
{
 return $this->id;
}

public function __toString()
{
 return $this->home_team .&#39; vs. &#39;. $this->away_team .&#39; at &#39;. $this->kickoff .&#39; <br /> &#39;. $this->getEventsAsString();
}

private function getEventsAsString( )
{
 $out = "";
 $events = parent::getAll();
 foreach ($events as $event ) {
  $out .= &#39;- &#39;. $event . &#39;<br />&#39;;
 }
 return $out;
}
}
?>

 

I&#39;m guessing I will find errors/weird behavior initially, but for now, it works REALLY great. By extending a simple Collections class, I can simply add, remove and retrieve objects in a very simple matter. The next implementation will be to create an iterator object inside the Collection for simple output of the data.

 

Application code would look like this:

<?php
// Create clubs
$juventus   =  new Club(1, "Juventus");
$barcelona  =  new Club(2, "Barcelona");
$real   =  new Club(3, "Real Madrid");
// Create players
$vuci = new Player(10, "Mirko Vucinic", 17, "Vucinic");
$del_piero = new Player(10, "Alessandro Del Piero", 10, "Del Piero");
$messi = new Player(10, "Leonel Messi", 10, "Messi");
$ronaldo = new Player(10, "Cristiano Ronaldo", 10, "Ronaldo");
// Create matches
$match1 = new Match(1, $juventus, $barcelona, &#39;2011-05-20 20:45&#39;, 2, 0);
$match2 = new Match(2, $real, $barcelona, &#39;2011-05-25 20:45&#39;, 0, 6);
// Create events
$match1->add(new Goal( $vuci, 40, $del_piero ));
$match1->add(new Goal( $del_piero, 60, $vuci ));
$match1->add(new Card( $vuci, 65, Card::$YELLOW_CARD));
$match1->add(new Card( $vuci, 75, Card::$RED_CARD));
echo $match1 . &#39;<br />&#39;;
// Create events
$match2->add(new Goal( $messi, 20 ));
$match2->add(new Goal( $messi, 30 ));
$match2->add(new Goal( $messi, 39, $ronaldo ));
$match2->add(new Card( $ronaldo, 39, Card::$YELLOW_CARD));
$match2->add(new Goal( $messi, 60 ));
$match2->add(new Card( $ronaldo, 61, Card::$RED_CARD));
echo $match2;
?>

 

This produces the following output:

Juventus vs. Barcelona at 2011-05-20 20:45

- Goal: Vucinic ( Assist: Del Piero ) 40&#39; min

- Goal: Del Piero ( Assist: Vucinic ) 60&#39; min

- Yellow card Vucinic 65

- Red card Vucinic 75

 

Real Madrid vs. Barcelona at 2011-05-25 20:45

- Goal: Messi 20&#39; min

- Goal: Messi 30&#39; min

- Goal: Messi ( Assist: Ronaldo ) 39&#39; min

- Yellow card Ronaldo 39

- Goal: Messi 60&#39; min

- Red card Ronaldo 61

 

And print_r:

MATCH 1:

 

Match Object
(
   [id:Match:private] => 1
   [home_team:Match:private] => Club Object
       (
           [id:Club:private] => 1
           [name:Club:private] => Juventus
       )

   [away_team:Match:private] => Club Object
       (
           [id:Club:private] => 2
           [name:Club:private] => Barcelona
       )

   [kickoff:Match:private] => 2011-05-20 20:45
   [home_goals:Match:private] => 2
   [away_goals:Match:private] => 0
   [number_of_events:Match:private] => 0
   [array:Collection:private] => Array
       (
           [1] => Goal Object
               (
                   [id:Goal:private] => 1
                   [goal:Goal:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Mirko Vucinic
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Vucinic
                       )

                   [time:Goal:private] => 40
                   [assist:Goal:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Alessandro Del Piero
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Del Piero
                       )

               )

           [2] => Goal Object
               (
                   [id:Goal:private] => 2
                   [goal:Goal:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Alessandro Del Piero
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Del Piero
                       )

                   [time:Goal:private] => 60
                   [assist:Goal:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Mirko Vucinic
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Vucinic
                       )

               )

           [3] => Card Object
               (
                   [id:Card:private] => 3
                   [player:Card:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Mirko Vucinic
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Vucinic
                       )

                   [time:Card:private] => 65
                   [type:Card:private] => 1
               )

           [4] => Card Object
               (
                   [id:Card:private] => 4
                   [player:Card:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Mirko Vucinic
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Vucinic
                       )

                   [time:Card:private] => 75
                   [type:Card:private] => 2
               )

       )

)
1

MATCH 2:

 

Match Object
(
   [id:Match:private] => 2
   [home_team:Match:private] => Club Object
       (
           [id:Club:private] => 3
           [name:Club:private] => Real Madrid
       )

   [away_team:Match:private] => Club Object
       (
           [id:Club:private] => 2
           [name:Club:private] => Barcelona
       )

   [kickoff:Match:private] => 2011-05-25 20:45
   [home_goals:Match:private] => 0
   [away_goals:Match:private] => 6
   [number_of_events:Match:private] => 0
   [array:Collection:private] => Array
       (
           [1] => Goal Object
               (
                   [id:Goal:private] => 1
                   [goal:Goal:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Leonel Messi
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Messi
                       )

                   [time:Goal:private] => 20
                   [assist:Goal:private] => 
               )

           [2] => Goal Object
               (
                   [id:Goal:private] => 2
                   [goal:Goal:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Leonel Messi
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Messi
                       )

                   [time:Goal:private] => 30
                   [assist:Goal:private] => 
               )

           [3] => Goal Object
               (
                   [id:Goal:private] => 3
                   [goal:Goal:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Leonel Messi
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Messi
                       )

                   [time:Goal:private] => 39
                   [assist:Goal:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Cristiano Ronaldo
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Ronaldo
                       )

               )

           [4] => Card Object
               (
                   [id:Card:private] => 4
                   [player:Card:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Cristiano Ronaldo
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Ronaldo
                       )

                   [time:Card:private] => 39
                   [type:Card:private] => 1
               )

           [5] => Goal Object
               (
                   [id:Goal:private] => 5
                   [goal:Goal:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Leonel Messi
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Messi
                       )

                   [time:Goal:private] => 60
                   [assist:Goal:private] => 
               )

           [6] => Card Object
               (
                   [id:Card:private] => 6
                   [player:Card:private] => Player Object
                       (
                           [id:Player:private] => 10
                           [name:Player:private] => Cristiano Ronaldo
                           [shirtname:Player:private] => 
                           [shirtnumber:Player:private] => Ronaldo
                       )

                   [time:Card:private] => 61
                   [type:Card:private] => 2
               )

       )

)

 

Sorry for the long post. I understand that it would be difficult to comment upon this without having more code, but that seems a little impractical.

I hope I've succeeded in demonstrating the simplicity in the class design. The question is: Why don't we see more code like this in PHP? I've never seen a Java-like Collection in PHP.

Why do you think that is? Do you understand how the code works, or is this not the way most PHP applications are coded? Hoping for some answers. :)

Link to comment
Share on other sites

Okay, so a couple of thoughts. First, the reason you may not see code like this in PHP like you do in Java is that they're different languages. Obviously. But specifically how objects are implemented in both languages differ. And the fact that PHP isn't natively an OO language, means that it's OO development isn't at the same place as Java's. Keep in mind that PHP only added comparable objects in version 5, and it's still adding common features like namespaces.

 

Second, have you looked into the Standard PHP Library (http://php.net/manual/en/book.spl.php)? It defines a lot of the functionality you're talking about, in a way that can easily be used in custom collections.

Link to comment
Share on other sites

And the fact that PHP isn't natively an OO language, means that it's OO development isn't at the same place as Java's. Keep in mind that PHP only added comparable objects in version 5, and it's still adding common features like namespaces.

 

I think you're right. Because "The Object class" is not the base class for all objects, you don't have a lot of the Java tools to implement or extend. Things like compareTo(), equals() and hashCode() are very important in Java, but a lot of this functionality is spread around in PHP, as you would more likely use an associative array than hashCodes in the object itself. I thought about another important difference yesterday, namely the super global arrays like POST and GET. In PHP, you can skip a lot of reference passing just because of this. When you have a native way of passing data, no need to build objects and pass those data along the line. It just makes sense.

 

Second, have you looked into the Standard PHP Library (http://php.net/manual/en/book.spl.php)? It defines a lot of the functionality you're talking about, in a way that can easily be used in custom collections.

 

I have not. Thank you, Larry. This is very familiar to me with structures such as SplStack, SplQueue, SplDoublyLinkedList, ArrayObject and so on. This is really what I've been looking for. The whole point of an array wrapper is to create easy to use functionality. A stack, a queque and maybe also the LinkedList is what I need. I don't LinkedLists are really that useful in PHP because of how the arrays works.Scratch that. If adding is done alphabetized, numerical or whatever your data's are, you could do cool things like binary search when getting elements. That would be really cool to implement.

 

I hope PHP will get closer to a strongly typed language as time goes, but still keep the simplicity that's one of PHP's strong suits. I would definitely like argument type hinting and return type hinting for the basic data structures like integers, doubles and strings. I find I need to check too much data right now because this is not part of the language.

 

Interesting topic, Larry. I will check out those classes and maybe publish some code if it seems about right. :)

Link to comment
Share on other sites

 Share

×
×
  • Create New...