Consider an object used to store a collection of items, but that collection may vary depending on predefined contexts.
Class Container implements IteratorAggregate (
protected $contexts; // list of associated contexts, example: array(0=>1,1=>3)
protected $contents; // array
public loadContents( $contextId ) { /* populates $this->contents*/ }
public getContexts() { /* populates $this->contexts */ }
...
public function getIterator() { return new ArrayIterator($this->contents); }
public getContextIterator() { return new contextIterator($this); }
}
The iterator looks like:
Class contextIterator {
protected $container;
protected $contexts;
protected $currentContext;
public function __construct($container) {
$this->container = $container;
$this->contexts = $container->getContexts();
$this->currentContext = 0;
}
public current() {
$this->container->loadContents( $this->key() );
return $this->contexts[ $this->key() ];
}
public function key() { return $this->currentContext; }
public function next() { $this->currentContext++; }
public function rewind() { $this->currentContext = 0; }
public function valid() { return isset( $this->contexts[ $this->key() ] ); }
}
For the few cases where each context needs to be examined iteratively, I do the following:
$myContainer = new Container();
foreach( $myContainer->getContextIterator() as $key => $value ) {
$myContainer->someMethod();
}
The above is nice and compact, but it feels dirty to me since I'm never actually using $key or $value. Is using the iterator overkill? Further, should an iterator ever change the state/contents of the object it is iterating?
The above is nice and compact, but it feels dirty to me since I'm never actually using $key or $value.
You have not shown the inners of getContextIterator() so it's hard to make concrete suggestions. Generally it's possible to create iterate-able objects in PHP by implementing the OuterIterator interace or by just implementing the Iterator interface. Both interfaces are predefined and you then can use your object with next(), foreach etc.
I assume you've implemented something like OuterIterator. If you implement OuterIterator instead, you will get some speed benefit AFAIK.
Is using the iterator overkill?
No, won't say so. Iterators are very good for collections as you said you have one. I just would change it into a SPL iterator though.
Further, should an iterator ever change the state/contents of the object it is iterating?
Well actually each iterator does, at least for the internal pointer of the iteration. But I think that was not your concern, but might already lighten up.
So even for "more" changes inside the object you're iterating over, it's perfectly okay that it changes as long as it's clear what it does. Counter-Example: if you iterate over an array and it would shuffle elements each time the iteration goes one step ahead would not be useful.
But there are other cases where this is totally valid and useful. So decide on what's done, not with a general rule.
Related
I have code that looks like this, which I'd like to improve:
// example type
class Stuff
{
public function __construct($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
}
// generator function
function searchStuff()
{
yield new Stuff('Fou');
yield new Stuff('Barre');
yield new Stuff('Bazze');
}
// code that iterates over the results of the generator
$stuffIterator = searchStuff();
assert($stuffIterator instanceof Iterator);
foreach ($stuffIterator as $stuff) {
/** #var Stuff $stuff */
echo $stuff->getName() . PHP_EOL;
}
The thing that I'd like to improve is the annotation in the loop (third last line), which I'd like to remove completely. The reasons are
it should be unnecessary with proper type hints that are even enforced by the language
it may or may not reflect reality, i.e. it is prone to break on code changes
it is unnecessary work typing it and, even worse, reading it.
My naïve approach was to declare an iterator interface that adds a proper type annotation to the generic Iterator interface:
interface StuffIterator extends Iterator
{
public function current(): Stuff;
}
This has the drawback that I can't set this as "hard" annotation on the function, only as docstring annotation, because "Generators may only declare a return type of Generator, Iterator, Traversable, or iterable", which is bad, because then it isn't enforced. Further, my IDE doesn't pick up the type, but that's a different issue.
A different approach was to write an actual iterator class that wraps the Generator returned from the function. Problem there is that this class needs to be instantiated as well, so I would have to call $stuffGenerator = new StuffIterator(searchStuff()); or write another wrapper function to do that, neither of which should be necessary. Still, the stupid IDE doesn't pick up the type hint (grrrr...!).
So, here's my question: What alternatives exist to this approach? I'd imagine something like C++ or Java generics, but alas, I can't simply rewrite the application in question.
Further notes:
The example code works, that's not the problem, my concerns are rather maintainability, readability and elegance.
I can't simply return an array, using a generator at this point is important. So, any suggestion based on this approach is not a solution.
I'm using PHP 7.1 at the moment, but I don't rule out upgrading. I'd consider an answer valid if it required upgrading, too.
a very good question. I guess the answer to your question will not turn out as you might expect. The solution is perhaps not nice, but works. First of all you can not define a yield return type other than Generator and so on. You have given the answer yourself. But ...
Just image the following starting point.
class Stuff
{
protected $name;
public function getName() : ?string
{
return $this->name;
}
public function setName(string $name) : Stuff
{
$this->name = $name;
return $this;
}
}
class StuffCollection extends \IteratorIterator
{
public function __construct(Stuff ...$items)
{
parent::__construct(
(function() use ($items) {
yield from $items;
})()
);
}
public function current() : Stuff
{
return parent::current();
}
}
What I 've done here? We know the Stuff class already. It does nothing new. The new thing is the StuffCollection class. Because of extending it from the IteratorIterator class we can override the IteratorIterator::current() method and give it a type hint.
$collection = new StuffCollection(
(new Stuff())->setName('One'),
(new Stuff())->setName('Two'),
(new Stuff())->setName('Three')
);
foreach ($collection as $item) {
var_dump(assert($item instance of Stuff));
echo sprintf(
'Class: %s. Calling getName method returns "%s" (%s)',
get_class($item),
$item->getName(),
gettype($item->getName())
) . "<br>";
}
The output from that should be ...
bool(true) Class: Stuff. Calling getName method returns "One" (string)
bool(true) Class: Stuff. Calling getName method returns "Two" (string)
bool(true) Class: Stuff. Calling getName method returns "Three" (string)
What does that mean? You really can not define the return type directly in a yield call. A yield will always return a Generator instance. One possible solution could be the use of the IteratorIterator class.
Even your IDE should work with that solution.
I'm starting to work with classes in PHP.
I have been reading and I noticed PHP is all about arrays.
So I was wondering if it would be a good practice to use the class properties inside array and naming them after keys.
Like this:
private $prefix;
private $name;
public function setPrefix($p)
{
$this->prefix = $p;
}
public function getPrefix()
{
return $this->prefix;
}
public function setName($n)
{
$this->name = $n;
}
public function getName()
{
return $this->name;
}
That's the common way of doing this.
But instead do it like this:
private $data = array();
public function setData($property, $value)
{
$this->data[$property] = $value;
}
public function getData($property)
{
return $this->data[$property];
}
Would this be better than the common way? I believe that would be a generic class structure for any database table.
Would this be better than the common way?
NO. And in fact it have drawbacks.
It removes the public, protected and private encapsulation of your properties (which is in the essence of oop).
Adds a layer over every variable access. I don't really know the internals of php, but I really don't think it could be faster than native properties. (although the difference is probably absolutely irrelevant to any script)
IDE's won't be able to complete your code when accessing properties.
It can have it's uses, if your class is a container which needs to have an array of internal data, in which case you would class container implements ArrayAccess and use it like an array, instead of global get/set methods. Here the documentation for ArrayAccess()
$obj = new container();
$obj['key'] = "value";
echo $obj['key'];
Bottom line
Why try and reinvent the wheel? A property is a property. There is no logical or semantical improvement in wrapping every property inside another property. It's obsfucating everything. It won't be faster, it won't be clearer, it removes the oop concepts from your properties and it's just going against the current of using objects in the first place.
About easier database management
If you really want to easily pass an array to a prepared statement, you can get the properties of an object with get_object_vars($obj), no need to put them in an array before for this very purpose. Moreover, as noted by Cypher, you won't be able to use the built-in fetchObject() method, which completely nullify the time you will not have gained by having an easier time querying the database.
This will make it easy to automate DB Operations.
But will make it hard for to use the object by humans.
Yii(2) uses this setup as part of there ActiveRecords but extend it by
defining the properties as a comment
/**
* #property int $id
* #property string $name
*/
class SomeClass extends AbstractModel
And also implements magic methods: __get(), __set()` so you can easily set and get properties like this:
class AbstractModel{
public function __get($name){
if(isset($this->data[$name])){
return $this->data[$name];
}else{
throw new Exception("Undefined or property '$name'");
}
}
public function __set($name, $value){
if(isset($this->data[$name])){
return $this->data[$name] = $value;
}else{
throw new Exception("Undefined or property '$name'");
}
}
Just to clarify, I mean something like:
class foon {
private $barn = null;
public function getBarn() {
if (is_null($this->barn)) {
$this->barn = getBarnImpl();
}
return $this->barn;
}
}
This is especially nice when you don't always need getBarn, and getBarn is particularly expensive (e.g. has a DB call). Is there any way to avoid the conditional? This takes up a lot of space, looks ugly, and seeing conditionals disappear is always nice. Is there some other paradigm to handle this lazy loading that I just can't see?
By using php's __call() magic method, we can easily write a decorator object that intercepts all method calls, and caches the return values.
One time I did something like this:
class MethodReturnValueCache {
protected $vals = array();
protected $obj;
function __construct($obj) {
$this->obj = $obj;
}
function __call($meth, $args) {
if (!array_key_exists($meth, $this->vals)) {
$this->vals[$meth] = call_user_func_array(array($this->obj, $meth), $args);
}
return $this->vals[$meth];
}
}
then
$cachedFoon = new MethodReturnValueCache(new foon);
$cachedFoon->getBarn();
I've wondered this from time to time, but I certainly can't think of one. Unless you want to create a single function to handle this with arrays and reflective property lookups.
You could do:
return $this->barn != null ? $this->barn : ($this->barn = self::getBarnImpl());
But I don't see how that's any better.
return ( $this->barn = $this->barn ? $this->barn : getBarn() );
or the php 5.3 (?) one:
return ( $this->barn = $this->barn ?: getBarn() );
I don't think I have ever seen a method for completely eliminating this type of lazy initialization checking, but it is interesting to think about. With a toy sample there doesn't seem to be any advantage, but in large objects you could refactor the lazy initialization behavior into either the object to be initialized or (more interestingly) some sort of generic lazy initializer pattern (I am picturing something roughly similar to a singleton). Basically unless they decide to build it in as a language construct (in which case it would still be there, only hidden) I think the best you can do is to encapsulate the code yourself.
class LazyObject
{
...
public function __construct($type, $args)
{
$this->type = $type;
...
}
public getInstance()
{
if (empty($this->instance))
$this->instance = new $this->type($args);
return $instance;
}
}
class AggregateObject
{
private $foo;
private $bar;
public function __construct()
{
$this->foo = new LazyObject('Foo');
$this->bar = new LazyObject('Bar');
}
public function getFoo()
{
return $this->foo->getInstance();
}
...
}
Method1
I can think of listener class.
Constructor () {
object = null
listener = new Object() { // this is called once
object = init()
listener = new Object() { // next time
do-nothing() // this is called
}
}
Object get() {
listener.invoke()
return object
This has no condition checkers but it adds an extra field to every object, effectively duplicaing the memory consumption whereas the stupid penalty of calling useless code, listener.invoke(), persists. I do not know how to remove it with all the polymorphysm. Because the get() method is shared by all instances of the class, it cannot be morphed.
Method2
Java on-demand initialization by exploiting the lazy class loading.
Bottom line
So, it looks like the alternatives are worse than the conditional because modern CPUs optimize the branch predictions. So, check penalty will be very tiny, I expect, once code is initialized and branch is always go into one direction. The false branch will be taken only once, at the initialization time, and it will also be short compared to your initialization time. Otherwise you may be do not want to defer the initialization.
PLEASE CHECK ANSWERS by VolkerK too, he provided another solution, but I can't mark two posts as answer. :(
Good day!
I know that C# allows multiple iterators using yield, like described here:
Is Multiple Iterators is possible in c#?
In PHP there is and Iterator interface. Is it possible to implement more than one iteration scenario for a class?
More details (EDIT):
For example I have class TreeNode implementing single tree node. The whole tree can be expressed using only one this class. I want to provide iterators for iterating all direct and indirect children of current node, for example using BreadthFirst or DepthFirst order.
I can implement this Iterators as separate classes but doing so I need that tree node should expose it's children collection as public.
C# pseudocode:
public class TreeNode<T>
{
...
public IEnumerable<T> DepthFirstEnumerator
{
get
{
// Some tree traversal using 'yield return'
}
}
public IEnumerable<T> BreadthFirstEnumerator
{
get
{
// Some tree traversal using 'yield return'
}
}
}
Yes, you can.
foreach(new IteratorOne($obj) as $foo) ....
foreach(new IteratorTwo($obj) as $bar) .....
Actually, as long as you class implements Iterator, you can apply any arbitrary IteratorIterator to it. This is a Good Thing, because applied meta iterators are not required to know anything about the class in question.
Consider, for example, an iterable class like this
class JustList implements Iterator
{
function __construct() { $this->items = func_get_args(); }
function rewind() { return reset($this->items); }
function current() { return current($this->items); }
function key() { return key($this->items); }
function next() { return next($this->items); }
function valid() { return key($this->items) !== null; }
}
Let's define some meta iterators
class OddIterator extends FilterIterator {
function accept() { return parent::current() % 2; }
}
class EvenIterator extends FilterIterator {
function accept() { return parent::current() % 2 == 0; }
}
Now apply meta iterators to the base class:
$list = new JustList(1, 2, 3, 4, 5, 6, 7, 8, 9);
foreach(new OddIterator($list) as $p) echo $p; // prints 13579
foreach(new EvenIterator($list) as $p) echo $p; // prints 2468
UPDATE: php has no inner classes, so you're kinda out of luck here, without resorting to eval, at least. Your iterators need to be separate classes, which are aware of the baseclass structure. You can make it less harmful by providing methods in the base class that instantiate iterators behind the scenes:
class TreeDepthFirstIterator implements Iterator
{
function __construct($someTree).....
}
class Tree
{
function depthFirst() { return new TreeDepthFirstIterator($this); }
....
}
foreach($myTree->depthFirst() as $node).....
Another option is to use lambdas instead of foreach. This is nicer and more flexible, requires php5.3 though:
class Tree
{
function depthFirst($func) {
while($node = .....)
$func($node);
.....
$myTree->depthFirst(function($node) {
echo $node->name;
});
For your purpose it might be sufficient to have a "mode" flag in your class, so the user can choose whether to have a bread-first or a depth-first iterator.
class Tree {
const TREE_DEPTH_FIRST = 0;
const TREE_BREADTH_FIRST = 0;
protected $mode;
protected $current;
public function __construct($mode=Tree::TREE_DEPTH_FIRST) {
$this->mode = $mode;
}
public function setMode($mode) {
...
}
public function next() {
$this->current = advance($this->current, $this->mode);
}
....
}
(and the short answer to your initial question: no php doesn't have the syntactic sugar of yield return and it doesn't have inner private classes, i.e. whatever you would need the iterator you're returning to do with the "original" object has to be exposed to the outside world. So you'd probably end up "preparing" all elements for an iterator object like ArrayIterator, the very thing you avoid by using yield)
This code shows you how to add multiple iterators in a class.
class TreeNode {
public function getOddIterator () {
return new OddIterator($this->nodes);
}
public function getEvenIterator () {
return new EvenIterator($this->nodes);
}
}
You can have multiple Iterators. The key idea in the Iterator is to take the responsibility for access and traversal out of the list object and put it into an iterator object. So if you want to have multiple Iterators with the same list or different lists; there's no problem.
You can find four different PHP examples here:
http://www.php5dp.com/category/design-patterns/iterator/
You can also use them with Linked Lists.
Cheers,
Bill
I'm trying to iterate over a directory which contains loads of PHP files, and detect what classes are defined in each file.
Consider the following:
$php_files_and_content = new PhpFileAndContentIterator($dir);
foreach($php_files_and_content as $filepath => $sourceCode) {
// echo $filepath, $sourceCode
}
The above $php_files_and_content variable represents an iterator where the key is the filepath, and the content is the source code of the file (as if that wasn't obvious from the example).
This is then supplied into another iterator which will match all the defined classes in the source code, ala:
class DefinedClassDetector extends FilterIterator implements RecursiveIterator {
public function accept() {
return $this->hasChildren();
}
public function hasChildren() {
$classes = getDefinedClasses($this->current());
return !empty($classes);
}
public function getChildren() {
return new RecursiveArrayIterator(getDefinedClasses($this->current()));
}
}
$defined_classes = new RecursiveIteratorIterator(new DefinedClassDetector($php_files_and_content));
foreach($defined_classes as $index => $class) {
// print "$index => $class"; outputs:
// 0 => Class A
// 1 => Class B
// 0 => Class C
}
The reason the $index isn't sequential numerically is because 'Class C' was defined in the second source code file, and thus the array returned starts from index 0 again. This is preserved in the RecursiveIteratorIterator because each set of results represents a separate Iterator (and thus key/value pairs).
Anyway, what I am trying to do now is find the best way to combine these, such that when I iterate over the new iterator, I can get the key is the class name (from the $defined_classes iterator) and the value is the original file path, ala:
foreach($classes_and_paths as $filepath => $class) {
// print "$class => $filepath"; outputs
// Class A => file1.php
// Class B => file1.php
// Class C => file2.php
}
And that's where I'm stuck thus far.
At the moment, the only solution that is coming to mind is to create a new RecursiveIterator, that overrides the current() method to return the outer iterator key() (which would be the original filepath), and key() method to return the current iterator() value. But I'm not favouring this solution because:
It sounds complex (which means the code will look hideous and it won't be intuitive
The business rules are hard-coded inside the class, whereas I would like to define some generic Iterators and be able to combine them in such a way to produce the required result.
Any ideas or suggestions gratefully recieved.
I also realise there are far faster, more efficient ways of doing this, but this is also an exercise in using Iterators for myselfm and also an exercise in promoting code reuse, so any new Iterators that have to be written should be as minimal as possible and try to leverage existing functionality.
Thanks
OK, I think I finally got my head around this. Here's roughly what I did in pseudo-code:
Step 1
We need to list the directory contents, thus we can perform the following:
// Reads through the $dir directory
// traversing children, and returns all contents
$dirIterator = new RecursiveDirectoryIterator($dir);
// Flattens the recursive iterator into a single
// dimension, so it doesn't need recursive loops
$dirContents = new RecursiveIteratorIterator($dirIterator);
Step 2
We need to consider only the PHP files
class PhpFileIteratorFilter {
public function accept() {
$current = $this->current();
return $current instanceof SplFileInfo
&& $current->isFile()
&& end(explode('.', $current->getBasename())) == 'php';
}
}
// Extends FilterIterator, and accepts only .php files
$php_files = new PhpFileIteratorFilter($dirContents);
The PhpFileIteratorFilter isn't a great use of re-usable code. A better method would have been to be able to supply a file extension as part of the construction and get the filter to match on that. Although that said, I am trying to move away from construction arguments where they are not required and rely more on composition, because that makes better use of the Strategy pattern. The PhpFileIteratorFilter could simply have used the generic FileExtensionIteratorFilter and set itself up interally.
Step 3
We must now read in the file contents
class SplFileInfoReader extends FilterIterator {
public function accept() {
// make sure we use parent, this one returns the contents
$current = parent::current();
return $current instanceof SplFileInfo
&& $current->isFile()
&& $current->isReadable();
}
public function key() {
return parent::current()->getRealpath();
}
public function current() {
return file_get_contents($this->key());
}
}
// Reads the file contents of the .php files
// the key is the file path, the value is the file contents
$files_and_content = new SplFileInfoReader($php_files);
Step 4
Now we want to apply our callback to each item (the file contents) and somehow retain the results. Again, trying to make use of the strategy pattern, I've done away unneccessary contructor arguments, e.g. $preserveKeys or similar
/**
* Applies $callback to each element, and only accepts values that have children
*/
class ArrayCallbackFilterIterator extends FilterIterator implements RecursiveIterator {
public function __construct(Iterator $it, $callback) {
if (!is_callable($callback)) {
throw new InvalidArgumentException('$callback is not callable');
}
$this->callback = $callback;
parent::__construct($it);
}
public function accept() {
return $this->hasChildren();
}
public function hasChildren() {
$this->results = call_user_func($this->callback, $this->current());
return is_array($this->results) && !empty($this->results);
}
public function getChildren() {
return new RecursiveArrayIterator($this->results);
}
}
/**
* Overrides ArrayCallbackFilterIterator to allow a fixed $key to be returned
*/
class FixedKeyArrayCallbackFilterIterator extends ArrayCallbackFilterIterator {
public function getChildren() {
return new RecursiveFixedKeyArrayIterator($this->key(), $this->results);
}
}
/**
* Extends RecursiveArrayIterator to allow a fixed $key to be set
*/
class RecursiveFixedKeyArrayIterator extends RecursiveArrayIterator {
public function __construct($key, $array) {
$this->key = $key;
parent::__construct($array);
}
public function key() {
return $this->key;
}
}
So, here I have my basic iterator which will return the results of the $callback I supplied through, but I've also extended it to create a version that will preserve the keys too, rather than using a constructor argument for it.
And thus we have this:
// Returns a RecursiveIterator
// key: file path
// value: class name
$class_filter = new FixedKeyArrayCallbackFilterIterator($files_and_content, 'getDefinedClasses');
Step 5
Now we need to format it into a suitable manner. I desire the file paths to be the value, and the keys to be the class name (i.e. to provide a direct mapping for a class to the file in which it can be found for the auto loader)
// Reduce the multi-dimensional iterator into a single dimension
$files_and_classes = new RecursiveIteratorIterator($class_filter);
// Flip it around, so the class names are keys
$classes_and_files = new FlipIterator($files_and_classes);
And voila, I can now iterate over $classes_and_files and get a list of all defined classes under $dir, along with the file they're defined in. And pretty much all of the code used to do this is re-usable in other contexts as well. I haven't hard-coded anything in the defined Iterator to achieve this task, nor have I done any extra processing outside the iterators
I think what you want to do, is more or less to reverse the keys and values returned from PhpFileAndContent. Said class returns a list of filepath => source, and you want to first reverse the mapping so it is source => filepath and then expand source for each class defined in source, so it will be class1 => filepath, class2 => filepath.
It should be easy as in your getChildren() you can simply access $this->key() to get the current file path for the source you are running getDefinedClasses() on. You can write getDefinedClasses as getDefinedClasses($path, $source) and instead of returning an indexed array of all the classes, it will return a dictionary where each value from the current indexed array is a key in the dictionary and the value is the filepath where that class was defined.
Then it will come out just as you want.
The other option is to drop your use of RecursiveArrayIterator and instead write your own iterator that is initialized (in getChildren) as
return new FilePathMapperIterator($this->key,getDefinedClasses($this->current()));
and then FilePathMapperIterator will convert the class array from getDefinedClasses to the class => filepath mapping I described by simply iterating over the array and returning the current class in key() and always returning the specified filepath in current().
I think the latter is more cool, but definitely more code so its unlikely that I would have gone that way if I can adapt getDefinedClasses() for my needs.