Reputation: 14906
I am finding a lot of the time I pass an array of custom objects to a function for processing. In my function I check to make sure it is the right class before accessing internal properties/methods.
Because I am taking an array and as far as I am aware PHP doesn't have any typed generic list I cannot use type hinting so intellisense in my IDE doesn't work and code inspection throws warnings.
I came across an old post that gave the idea of throwing in this line of code in order to get intellisense working while developing:
if (false) $myObj = new MyObject();
So I end up with a function that looks like:
function ProcessObjectArray(array $arrayOfObject)
{
foreach ($arrayOfObject as $key => $myObj) {
if (get_class($myObj) == 'MyNamespace\MyObject') {
if (false) $myObj = new MyObject();
// I can now access intellisense for MyObject in here
} else {
trigger_error('is not right object');
}
}
}
It seems like a bit of a weird hack so I am wondering if this is a sign that I am not handling arrays of objects in the best way or if there is a better way to get my intellisense working. I had a look at the arrayobject interface to see if I could create a class implementing arrayobject that would hold a typed list. While it makes it nice to put all validation inside its constructor or append like functions I couldn't get it working with intellisense as the internal container is still just a standard array. Also using get_class
doesn't seem good as if a class name or namespace is renamed then the IDE refactoring features do not pick this reference up (well, PHPStorm doesn't at least and it is one I am using).
I of course don't need intellisense but the weirdness of this hack made me wonder if I have the right approach to OOP in PHP and thought I might be missing something. Is this the right way of doing it or have I missed something?
Upvotes: 3
Views: 713
Reputation: 3967
class MyCollection implements \Iterator
{
/**
* @var CollectionElement[]
*/
private $elements = [];
public function addElement(CollectionElement $element)
{
$this->elements[] = $element;
}
/**
* @return CollectionElement
*/
public function current()
{
return current($this->elements);
}
//... Rest of the Iterator implementation
}
This way you cannot add to the collection anything other than CollectionElement
. And after:
foreach($collection as $element){
/** @var CollectionElement $element */
$element->//here you will have autocompletion
}
You don't need to check anything since your collection will never contain anything you are not expecting.
Upvotes: 2
Reputation: 628
If you are going this route, I would avoid using the are native arrays. I would make classes that contain arrays of a certain type. That way, you can validate the array contents in the constructor of the class, and then you can use actual type hinting for the objects, which PHP allows.
Upvotes: -5
Reputation: 2877
with phpstorm when you add your annotations it will correctly do the code completion for you.
/**
* @param MyNamespace\MyObject[] $arrayOfObject
*/
function ProcessObjectArray(array $arrayOfObject)
{
foreach ($arrayOfObject as $key => $myObj) {
if (get_class($myObj) == 'MyNamespace\MyObject') {
if (false) $myObj = new MyObject();
// I can now access intellisense for MyObject in here
} else {
trigger_error('is not right object');
}
}
}
However intellisense won't stop you from passing in incorrect objects, it will only help you while coding to prevent passing in the incorrect objects.
Perhaps you may wish to consider using collections instead of using generic arrays.
There are lots of examples out there, however here is a simple one which you can expand upon
class Collection
{
/** @var array */
private $items = [];
/**
* @param MyNamespace\MyObject $obj
* @return $this
*/
public function addItem(MyNamespace\MyObject $obj) {
$this->items[] = $obj;
return $this;
}
/**
* @param $index
* @return $this
*/
public function deleteItem($index) {
unset($this->items[$index]);
return $this;
}
/**
* @param $index
* @return MyNamespace\MyObject|null
*/
public function getItem($index) {
return array_key_exists($index, $this->items) ? $this->items[$index] : null;
}
/**
* @return MyNamespace\MyObject[]
*/
public function getItems() {
return $this->items;
}
/**
* @return int
*/
public function count() {
return sizeOf($this->items);
}
}
here is an example how it all works:
$collection = new \Collection;
$obj = new MyNamespace\MyObject;
$collection->addItem($obj);
foreach($collection->getItems() as $item) {
// this will already know that $item is MyNamespace\MyObject because of the annotation on the getItems() method
$item->doSomething();
}
Upvotes: 4