Implementing a .Net Collection like class in PHP - php

So, i would like to implement something like this:
class Collection{
private $array;
public function add($object){
array_push($this->array, $object);
}
public function array(){
return $this->array;
}
}
class JustaClass{
public $myCollection;
public function __construct(){
$this->myCollection = new Collection();
}
}
$justAnObject = new JustaClass();
$justAnObject->myCollection->add(new SomeObject());
this works just fine, but i would like to work with it like i do in .Net, ie, when i want to refer to the collection Object, i would like to do it directly, like:
foreach($justAnObject->myCollection as $collectionItem)
and not like
foreach($justAnObject->myCollection->array() as $collectionItem)
Is there any way I can do this? maybe a magic method, or implementing an Iiterator-like interface?
thanks

Actually, this is what SplObjectStorage does, so no need to code anything:
The SplObjectStorage class provides a map from objects to data or, by ignoring data, an object Set. This dual purpose can be useful in many cases involving the need to uniquely identify objects.
It implements Countable, Iterator and ArrayAccess, so you can foreach it, access it with [] and use count on it. Like the description says it's a Set, so it contains no duplicate elements.
If you want to allow for duplicate elements, you can simply use ArrayIterator or ArrayObject. You can find additional Data Structures similar to the various .NET collections in
http://php.net/manual/en/spl.datastructures.php
IMO, there is no point in writing a custom class unless you also need to customize behavior of any of the options mentioned above.

Let your Collection class implement the Iterator or IteratorAggregate interfaces. There's also an ArrayIterator class, so it's really as easy as just returning an instance of that class:
class Collection implements IteratorAggregate {
private $array;
public function add($object){
array_push($this->array, $object);
}
/* required by IteratorAggregate */
public function getIterator() {
return new ArrayIterator($this->array);
}
}
You can then use your class in the following way:
$c = new Collection();
$c->add(1);
$c->add(2);
$c->add('we can even add strings');
foreach($c as $v) {
doSomething($v);
}

Related

PHP return array if breaking chain in singleton

I've built a singleton class with chaining methods (to be used in a template).
To make chaining work I need to return new static. It allows the next chain to be added. The problem I have is that I don't want to return the static object if there are no more chains.
Example
<?php
class bread {
public static $array;
public static function blueprints() {
static::$array = array('some', 'values');
return new static;
}
public static function fields() {
return static::$array;
}
}
$blueprints = bread::blueprints();
$fields = bread::blueprints()->fields();
print_r($blueprint) // Returns object - FAIL
print_r($fields ) // Returns array - OK
In the example above I want $blueprints to return an array, because there are no more methods chained on it.
How can that be done?
The simple answer is you cannot do what you want.
Method chaining is not a special thing for Php.
For your example
bread::blueprints()->fields();
This is not different than:
$tmp = bread::blueprints();
$tmp->fields();
So because of the Php does not know the context where the result will be used of it cannot change the return type.
Here is another version of this question:
Check if call is method chaining
However, your class can implement ArrayAccess interface.This will allow you to treat the object like an array without casting and you get total control over how the members are used.
You can try this:
$blueprints = (array)bread::blueprints();

Parameter to define a type of class

I have a constructor that asks for a type of class, but it doesn't define that as a type hint. You are able to pass anything you want to it, and it will accept it. Is there a way to pass a class type to the constructor, and in the add() method it only accepts that type?
Currently what I have, is the ability to pass anything to the constructor such as an int, string, bool, etc. Is there a way to make it so that the constructor only accepts class types?
class Main{
protected $items = [];
protected $type = '';
public function __construct($type){
$this->type = $type;
}
public function add($object){
if($object instanceof $this->type){
$this->items[] = $object;
}
}
}
class Test{}
class Awesome{}
$main1 = new Main(Test::class);
$main2 = new Main(Awesome::class);
// Successful:
$main1->add(new Test());
// Fail:
$main1->add(new Awesome());
// Successful:
$main2->add(new Awesome());
// Fail:
$main2->add(new Test());
If I were to do it in C# it would look something like this:
Main main1 = new Main<Test>();
Main main2 = new Main<Awesome>();
Basically it says that add() will only allow instances of Test. Is there a way to do some
Php doesn't support template like declarations like e.g. c++.
The best way you may be able to achive this is by passing a lambda which then in return gets used in order to validate the passed parameter in add.
<?php
class Test {
private $validator = null;
public function __construct($validator) {
$this->validator = $validator;
}
public function add($value) {
$func = $this->validator;
$validated = $func($value);
echo $validated ? 'OK' : 'NG';
}
}
$obj = new Test(function($value) {
return is_int($value);
});
$obj->add(11);
$obj->add('string');
Another possibility would be to pass the type e.g. "ClassName" in your constructor and use get_class() and gettype() for the validation.
In the future there may be smarter solutions since you'll be able to write anonymous classes but I haven't really thought about that but in the end they would work similarly to lambdas.
Basically it says that add() will only allow instances of Test.
It's possible to achieve this in PHP by simply adding the type before the argument name in the function definition (similar with C/C++/C# types):
class Main {
protected $items = [];
public function add(Test $object) {
$this->items[] = $object;
}
}
PHP 5 accepts classes, interfaces, array and callable as type hints. If Test is a class then Main::add() accepts objects of class Test and its children. If Test is an interface, then the method Main::add() accepts objects that implement Test or one of its children.
PHP 7 (coming soon to a server near you) introduces type hinting for scalar types too.
PHP does not support anything similar with C++ templates or C# generics. If you want to create a class that works with objects of type A and another class that has identical behaviour but works with objects of type B you have several options but none of them is as elegant as the templates/generics:
Create two classes having identical behaviour, one for objects of type A, another for objects of type B; use different type hints (A and B) in the arguments lists of the methods of the two classes to enforce the separation - not scalable;
Something similar to your code, use the allowed class name as a string property and check it on any operation; you can also validate the argument of the constructor using class_exists() - the code becomes cluttered with tests and less readable;
Use OOP polymorphism; extend both A and B from the same class T or, even better, make A and B implement the same interface I. A PHP interface can be empty, it doesn't need to declare anything; empty interfaces used just for type hinting are common practice in PHP.
Then write a single class Main and use I as type hint for all its methods that accept objects. It will accept objects of both types A and B but if you also declare functions in I (and implement them in A and B, of course) then use them in Main you can be sure nothing breaks (I becomes a contract between Main and the objects its accepts as arguments for its methods).
I would choose option #3 because it gets the most help from the interpreter; it verifies the type of the arguments on each function call that has type hints and triggers a recoverable fatal error (in PHP 5) or throws an exception (in PHP 7).
Also some IDEs and static code analysis tools can validate the calls without running the code and help you fix it.
Is there a way to make it so that the constructor only accepts class
types?
Nope!
It is not possible in PHP. Not like C#, at least.
You need either set a type hint or set any types.
However, there's a closer solution in order to accept only class when instancing a class: Using ReflectionClass!
class Main {
protected $items = [];
protected $type = null;
public function __construct($type) {
$reflector = new ReflectionClass($type);
$this->type = $reflector->getName(); # or: $this->type = $type;
}
public function add($object) {
if($object instanceof $this->type) {
$this->items[] = $object;
}
}
}
As ReflectionClass contructor argument only accpets a string containing the name of the class to reflect, you can take advantage that, so passing scalars strings will cause an exception.
$main = new Main(Test::class); # Okay!
$main = new Main('Test'); # Okay!
However
$main = new Main('bool');
// Results
# PHP Fatal error: Uncaught exception 'ReflectionException'
# with message 'Class bool does not exist' in ...
Change your constructor to this:
public function __construct(Type $type){
$this->type = $type;
}
This is based on the assumption that $type is an instance of Type.

PHP array with only objects of a specific class

I have a class called Rule and I'm about to create a RuleContainer class that's actually an array of Rule objects.
I wonder if there is an alternative of creating a new class. Is there any (modern) way to approach this problem? That is, something like using SPL to define an array that only allows adding objects of a specific class.
If not, which interface should I implement in my RuleContainer class?
The most suitable class for your task would be SplObjectStorage, but it doesn't allow for class typehint.
I think, you could do as follow:
class RuleContainer extends SplObjectStorage
{
function attach(Rule $rule)
{
parent::attach($rule);
}
function detach(Rule $rule)
{
parent::detach($rule);
}
}
and so on. You can read for SplObjectStorage interface on php.net and decide, what will you use and what needs overriding.
In your case, I would implement the Iterator interface in the RuleContainer, as I've done several times when I needed a sort of Collection<T> as we know it from other (typed) languages. And in the add(Rule $item) or addItem(Rule $item) method I'd make sure with the type definition of the argument (or using instanceof) that the item to be added is of type Rule.
Depending on the usage patterns for your container class, you need to implement one or more of these interfaces:
Iterator - to use it as foreach($container as $key => $value);
Countable - for count($container);
ArrayAccess - for $container[$key] (set it, get it, check if it isset(), unset() it);
Usage of PHP array-routines interfaces
You may achieve your goal with, for example, ArrayAccess implementation. Together with Iterator it will look like:
class ArrayStorage implements Iterator, ArrayAccess
{
private $holder = [];
private $instanceName;
public function __construct($instanceName)
{
if (!class_exists($instanceName)) {
throw new \Exception('Class '.$instanceName.' was not found');
}
$this->instanceName = $instanceName;
}
public function rewind()
{
reset($this->holder);
}
public function current()
{
return current($this->holder);
}
public function key()
{
return key($this->holder);
}
public function next()
{
next($this->holder);
}
public function valid()
{
return false !== $this->current();
}
public function offsetSet($offset, $value)
{
if (!($value instanceof $this->instanceName)) {
throw new \Exception('Storage allows only '.$this->instanceName.' instances');
}
if (is_null($offset)) {
$this->holder[] = $value;
} else {
$this->holder[$offset] = $value;
}
}
public function offsetExists($offset)
{
return isset($this->holder[$offset]);
}
public function offsetUnset($offset)
{
unset($this->holder[$offset]);
}
public function offsetGet($offset)
{
return isset($this->holder[$offset]) ? $this->holder[$offset] : null;
}
}
Procs
So - yes, you are doing instanceof check explicitly, but end user of your class doesn't know about that. It will only be possible to operate on valid instances in context of this storage (you can check this fiddle for usage sample). Concept is like:
$storage = new ArrayStorage('Foo'); //define what we will accept
$storage[] = new Foo; //fine, [] array-writing
$storage['baz'] = new Foo; //fine, key set
foreach ($storage as $key => $value) {
echo($key. ' => '.PHP_EOL.var_export($value, 1).PHP_EOL);
}
//invalid, will not pass. Either throw exception or just ignore:
$storage['bee'] = new Bar;
End fail-check behavior is up to you, but, my opinion, throwing exception is the best choice here as they are catchable, thus, end user may decide what to do in this case. Further option may be to add Countable to the storage, but it won't change generic idea.
And cons
Downside - no, you will not be able to "typehint" it somehow. While it is useful, in doc blocks you still will need to show what kind of entity are you accepting. In terms of general language features, there is arrayof RFC, by Joe Watkins, which was proposed for PHP version 5.6, but, unfortunately, failed. May be it will be reconsidered in next versions releases.
You can make RuleContainer yourself (as you say) and do all sorts of cleverness to manually enforce it but you live in the real world I live in the real world and you simply don't need a container object for this, just use an array.
If your problem is simply one of enforcement of the subject object type a lá List<className>() you can't do this in PHP and to be honest it's of debatable use in the languages where it is found (I know I will get down voted for saying this, but I will still be right) //excepting it helps further clarify the purpose of the list// In all honesty my 20+ years of programming across almost all the languages there is (except machine code perl and fortran), I can tell you such constructs and simply not worth the human overhead and themselves can include indirect unintended burdens way over their actual worth:
An easy compromise is: no laziness, start naming the array more than something like tmpList and if you are absolultey determined implment a simple test to http://php.net/manual/en/function.get-class.php at the start of the forloops you surely eventually use

PHP: Appending objects in OOP?

I've transitioned my coding step by step into OOP, which feels great. What I haven't got a hold of yet, is when It's time to use an object and when to use the conventional arrays.
Let's say that I would have the following code. Here we create an array containing Persons - receivers of emails. It's really cute, but it feels kind of smelly to jump back and forwards between arrays and objects.
Are my concerns legitimate or is this good practice?
public function receiver($email, $name = FALSE) {
$person = new Person();
$person->email = $email;
$person->name = $name;
$this->receiver[] = $person;
}
To answer your exact question, yes, using arrays is fine. You could use something like SplObjectStorage instead, but for most use-cases, an array is quite fine...
I would do this differently. Instead of creating the object inside the method, I'd pass the object in:
public function receiver(Person $person) {
$this->receiver[] = $person;
}
Then calling with:
$person = new Person();
$person->email = 'email#example.com';
$obj->receiver($person);
That way, you're decoupling the implementation of the Person object from the current object (whetever it is). Even better would be to declare an interface depending on how you expect it to work for that usage, and require an implementation of that interface:
interface iEmailable {
public function getName();
public function getEmail();
}
class Person implements iEmailable {
protected $name = '';
protected $email = '';
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
}
public function getName() {
return $this->name;
}
public function getEmail() {
return $this->email;
}
}
then, adjust your method to:
public function receiver(iEmailable $person) {
$this->receiver[] = $person;
}
Now, you're much more decoupled from the concrete Person class. Any class is free to be passed in, as long as it implements the proper interface...
That's absolutely fine, OOP doesn't preclude using arrays where appropriate.
In fact, arrays are often found hiding in OOP objects. The goal of classes/objects is to describe the properties of something. If that something needs to store multiple values of the same type, then there totally could be an array in there.
For instance, a class describing a Car/Automobile, might have an array that stores Seats.
There is nothing wrong with arrays, and if you have that idea then it is possible that you can "over-objectify" your code. Some things just don't make sense to create an object for.
In your example, it looks like your object has a property called receiver, as well as a method called receiver. That isn't the best of practices... keep your method and property names distinct.
As for arrays and objects and whatnot, look in to the Iterator (click for docs) interface. This allows you to treat an object like an array in foreach loops and the like.

Is it bad practice to construct a child class with a parent object?

I have a search class that I am using to fetch results from two different sources and combine them together. The Search class is the parent and has two children A and B which extend Search.
In the Search class, I have a method called fetch() which instantiates the two child objects to get their results. It looks something like this:
public function fetch(){
$a = new A($this);
$a_results = $a->fetch();
$b = new B($this);
$b_results = $b->fetch();
// code to combine the results here
}
The constructor of class A and B both look like this:
class A extends Search
{
public function __construct(Search $search){
parent::__construct($search->category, $search->offset, $search->keywords...);
}
It feels like I'm doing something wrong in that I'm passing a parent object to a child and then creating another parent object with the exact same data. Is there a better way to set this up?
I have it set this way because some parts of my application need to access class A and B directly, rather than through the parent Search class.
Use composition, for example have the Search class to have an array of sources, where each source is an instance of a Source class where you define what's common to a source and pass the parameters for each A and B sources.
The idea here, in case it's not clear, is for the Source class to return the data from the sources and let the Search class do the search. How practical or efficient this is depends on the actual source and way of searching
class Search {
private $sources = array();
public Search($p1,$p2,$p3,$p4) {
//Use proper parameters to define the sources
$sources[] = new Source("A",$p1,$p2,$p3,$p4);
$sources[] = new Source("B",$p1,$p2,$p3,$p4);
}
public function fetch() {
foreach ($source in $sources) {
$results[] = $source->fetch();
}
combine($results);
}
}
class Source {
//Whatever you need to define the source
public function fetch() {
//Fetch from the proper source
}
public Source($name,$p1,$p2,$p3,$p4) {
//Store the parameters to be able to operate
}
}

Categories