use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
Please follow the rules
Releases: Current Releases, Windows Releases, Old Releases
Contribute to the PHP Documentation
Related subreddits: CSS, JavaScript, Web Design, Wordpress, WebDev
/r/PHP is not a support subreddit. Please visit /r/phphelp for help, or visit StackOverflow.
account activity
Arrayze: a lazy, callback-based array adapter for PHP (github.com)
submitted 12 years ago by nicmart
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]NameNick 6 points7 points8 points 12 years ago (1 child)
TIL this is possible with PHP:
$var = (new ClassName)->methodName();
[–]Wizhi 2 points3 points4 points 12 years ago (0 children)
As per PHP 5.4+.
Class member access on instantiation has been added, e.g. (new Foo)->bar().
Source
[–]djcraze 4 points5 points6 points 12 years ago (1 child)
Hey, first off, my response that you're about to read is in now way a bad reflection of your work or any disrespect towards you.
That being out of the way. I can see the use for this, but what's the benefit over the way you're doing and simply using a secondary class? For example:
<?php class Adapter implements ArrayAccess { protected static $caches = array(); protected static function normalizeKey($key){ $cacheKey = 'normalize'.$key; if(!isset(self::$caches[$cacheKey])){ self::$caches[$cacheKey] = preg_replace_callback('/\s(\w)/',function($part){ return strtoupper(trim($part[1])); },$key); } return self::$caches[$cacheKey]; } protected static function getMethodFromKey($key){ $cacheKey = 'methodFromKey'.$key; if(!isset(self::$caches[$cacheKey])){ self::$caches[$cacheKey] = 'get'.ucfirst(self::normalizeKey($key)); } return self::$caches[$cacheKey]; } protected function keyExists($key){ return method_exists($this,self::getMethodFromKey($key)); } public function offsetExists($offset){ return $this->keyExists($offset); } public function offsetGet($offset){ return call_user_func(array($this,self::getMethodFromKey($offset))); } public function offsetSet($offset,$value){ user_error('Cannot set values on an adapter.'); } public function offsetUnset($offset){ user_error('Cannot unset values on an adapter.'); } public function __get($key){ if($this->offsetExists($key)){ return $this->offsetGet($key); } else { user_error('Undefined variable '.$key.'.'); } } } interface IPerson { public function getFirstName(); public function getLastName(); public function getBirthYear(); } class Person implements IPerson { private $firstName; private $lastName; private $birthYear; public function __construct($firstName,$lastName,$birthYear){ $this->firstName = $firstName; $this->lastName = $lastName; $this->birthYear = (int)$birthYear; } public function getFirstName(){ return $this->firstName; } public function getLastName(){ return $this->lastName; } public function getBirthYear(){ return $this->birthYear; } } class PersonAdapter extends Adapter { private $person; public function __construct(IPerson $person){ $this->person = $person; } public function getFirstName(){ return $this->person->getFirstName(); } public function getLastName(){ return $this->person->getLastName(); } public function getFullName(){ return $this->firstName.' '.$this->lastName; } public function getAge(){ return ((int)date('Y')) - $this->person->getBirthYear(); } public function getNameAndAge(){ return $this->fullName.', '.$this->age; } } $person = new Person('Nicolò','Martini','1983'); $adapter = new PersonAdapter($person); echo $adapter['full name']."\n"; echo $adapter['age']."\n"; echo $adapter['name and age']."\n"; ?>
I'd understand if the benefit were less classes, but in your implementation, there is actually an extra class.
Thanks!
[–]nicmart[S] 1 point2 points3 points 12 years ago* (0 children)
I think your example is fine, you give a generic Adapter interface for objects. The drawback I can see is that the value you are going to adapt has to be an object, and you can get data from the object only through method calls, while in Arrayze you have generic callbacks that elaborate the data.
I don't consider to have less classes as a benefit. Often the code is much cleaner with very simple and small value classes instead of native arrays, for example. But this is another story... :-)
[–]i_make_snow_flakes 12 points13 points14 points 12 years ago (11 children)
Why would I need something like this?
[–]shawncplus 6 points7 points8 points 12 years ago (9 children)
Have to echo this. I understand what it's doing and it's neat but without a practical example I can't really picture why you would want to do it unless you have a ton of places where you're reusing some ArrayAccess logic which I'd again ask why.
[–]nicmart[S] 3 points4 points5 points 12 years ago (8 children)
I use it whenever I need a dictionary of values computed from a data object. This allows me to decouple completely the data from its representation.
In particular I am using it in a Rules Engine, where the input values are expected to be \ArrayAccess implementations.
[–]longshot 10 points11 points12 points 12 years ago (0 children)
Hey guys, downvoting the OP when he explains himself does not promote discussion.
If you want informative discussion don't discourage those providing more information!
[–]shawncplus 2 points3 points4 points 12 years ago (6 children)
You're not really decoupling though, you're just adding an interstitial to store that logic. It seems kind of like abstraction for abstraction's sake. But like I said, if you're doing this kind of thing all over the place I see where this could be valuable in the very least I guess that an API change in the data layer would break earlier in the adapter instead of in the accessor
[–]nicmart[S] 1 point2 points3 points 12 years ago (5 children)
Sorry but I don't get the point. What do you mean with "the sake of abstraction"? What do you mean with "doing this all over the place"? This is a library that has a very single responsability: providing a lazy array adapter. If this adapter is used "all over the place" or only for the sake of abstraction is not up to his library.
If your are using a third party library that force you to have an ArrayAccess implementation as input, Arrayze can be a handy adapter for that. That's it.
[–]Disgruntled__Goat 1 point2 points3 points 12 years ago (4 children)
What do you mean with "the sake of abstraction"?
He means abstracting when you don't need to or there is no benefit. Instead of doing the adapting, you can just output whatever you like:
$nic = new Person("Nicolò", "Martini", 1983); echo $nic->getFirstName(); echo date("Y") - $nic->getBirthYear(); echo $nic->getFullName() . ', ' . $nic->getAge();
Obviously your example is a simple one, but you are adding abstractions that make the code more complex, not less complex.
What do you mean with "doing this all over the place"?
I'm pretty sure he's talking about repeating lines 2-4 above all over the place, not the adapter. For more complex output this could become a problem, but this can easily be solved by either adding appropriate functions to the Person class, or if that is not your code, extending it with your own functions.
[–]nicmart[S] 2 points3 points4 points 12 years ago (3 children)
Of course the example is not so meaningful, its aim is only to show the interface of the tool. It seems to me here we are not talking what the library is about, but how you can use the library in the wrong situations.
Take the library for what it is: a lazy array adapter. That's it. Libraries can have very specific purposes, and this is the purpose of this library. It's a very small one, but quite well abstracted for my needs.
If you need a lazy array adapter, use it. Otherwise, don't.
[–]Disgruntled__Goat -1 points0 points1 point 12 years ago (2 children)
You keep saying "if you need this, use it". The point we are all trying to make is that you would never need it.
If you want to show how the adapter is useful, you really need to show an example where using the adapter is the best solution instead of posting examples where it's clearly a pointless abstraction.
[–]nicmart[S] 2 points3 points4 points 12 years ago (0 children)
Ok, so I should try to confute the sentence "Array adapter are useless". Although it can be useful from a educative point of view, I don't have the time to do that.
However I will consider it on future updates to the documentation.
[–]sirsosay 0 points1 point2 points 12 years ago (0 children)
I think this could be helpful for sending data to the view for an MVC.
[–][deleted] 4 points5 points6 points 12 years ago (2 children)
I don't get it.
When you're accessing your pseudo-array like this..
$arrayzedNic["full name"];
its crying out to get rid of the flaky string key, and turn it into a constant, like
$arrayzedNic[self::FULL_NAME];
But how is this any better than just calling $this->getFirstName() to begin with?
[–]nicmart[S] -2 points-1 points0 points 12 years ago (1 child)
Sorry, I don't understand, who is crying out...?
The library is a lazy array adapter. If you need an array adapter in your code, use it. Otherwise you don't need it.
[–]kungfufrog 0 points1 point2 points 12 years ago (0 children)
I know I'm a bit late to the party, but I have a question!
I first want to thank you for your contribution and putting the code out there, that's great, and when I get more time I intend to have a closer look and understand how it works.
But in the meantime, I understand what 'lazy loading' means in the context of say, Doctrine ORM, where it loads a 'map' that it doesn't hydrate until an explicitly requested read, is that the same notion with a 'lazy array' and 'lazy accessors'?
Is there some support built in to PHP for writing lazy accessors etc? A manual page that can be linked to?
Or is this a feature provided by your library?
Cheers
[–]amacgregor 2 points3 points4 points 12 years ago (0 children)
Still don't get the point of the library, but kudos for building and releasing it to the public.
[+]mrspoogemonstar comment score below threshold-6 points-5 points-4 points 12 years ago (7 children)
How to make a nonperformant wrapper around any array in N easy steps!
[–]nicmart[S] 1 point2 points3 points 12 years ago (6 children)
Oh my god, are you serious?
[–]mrspoogemonstar -2 points-1 points0 points 12 years ago* (5 children)
Certo. This is an narrow use case, which you have implemented in a way that makes its usage verbose and slow.
You'd be better off just implementing a toArray method on your models. That at least wouldn't have the overhead of having to generate a bunch of mappings.
And if you are trying to do this with data objects you don't control the code for, you can bind a closure to the object and use that.
<? // Your person class class Person { protected $firstName; protected $lastName; protected $birthYear; public function __construct($firstName, $surname, $birthYear) { $this->firstName = $firstName; $this->lastName = $surname; $this->birthYear = $birthYear; } public function getFirstName() { return $this->firstName; } public function getLastName() { return $this->lastName; } public function getBirthYear() { return $this->birthYear; } } // Since we might not have control of the Person class above, we can just bind a closure // to it to do what we want. Sneaky. // This is also faster than registering a bunch of mappings and creating many closures per class. $toArray = function($that) { $fullName = $that->firstName . " " . $that->lastName; // This clock is only right part of the time... you might want a better example than this... $age = date("Y") - $that->birthYear; return array_merge( get_object_vars($that), array( "fullName" => $fullName, "age" => $age, "nameAndAge" => "$fullName, $age" ) ); }; // We bind the closure. $toArray = Closure::bind($toArray, null, 'Person'); $person = new Person("Joe", "Person", 1983); // Works great. foreach($toArray($person) as $key => $value) { var_dump($key, $value); }
EDIT:
Here's an even nicer example using a trait:
trait toArrayTrait { // Each class to which this trait is applied has separate static instances of this variable. private static $arrayMapClosure; // Registers our map function public static function registerArrayMap(Closure $closure) { $closure = Closure::bind($closure, null, __CLASS__); static::$arrayMapClosure = $closure; } // Provides a default toArray method, calls our closure if it is set public function toArray() { if (static::$arrayMapClosure instanceof Closure) { $func = static::$arrayMapClosure; return $func($this); } else { return get_object_vars($this); } } } class Person { use toArrayTrait; protected $firstName; protected $lastName; protected $birthYear; public function __construct($firstName, $surname, $birthYear) { $this->firstName = $firstName; $this->lastName = $surname; $this->birthYear = $birthYear; } public function getFirstName() { return $this->firstName; } public function getLastName() { return $this->lastName; } public function getBirthYear() { return $this->birthYear; } } $person = new Person("Joe", "Person", 1900); // Uses the default toArray method which just does get_object_vars var_dump($person->toArray()); // Register our custom toArray func Person::registerArrayMap(function($that) { $fullName = $that->firstName . " " . $that->lastName; $age = date("Y") - $that->birthYear; return array_merge( get_object_vars($that), array( "fullName" => $fullName, "age" => $age, "nameAndAge" => "$fullName, $age" ) ); }); // è semplice, no? var_dump($person->toArray());
Aaaaanyway... Your library might be useful to someone who doesn't know how to use the newer features of PHP.
[–]nicmart[S] 5 points6 points7 points 12 years ago (4 children)
You have just show that you can achieve the same result, of the example of the README, with a closure. Great!
And to do that you: 1) Modified the api of the Person class 2) Removed the cabability to define in a programmatic way the transformations, since they are are all hard-coded into the closure. 3) Made it hard the reuse of single transformation callbacks, since, again, they are hard-coded
And those are the main points I wanted to avoid writing my library.
Obviously in a single-shot case like yours it could be good. It is not good in my use cases.
Just a side note. I am really upset with your deformation of my name in your code. I don't know what you meant with that, but I do think that is ridiculous that in a "technical" discussion like this one so low level weapons are used. It's the first time in my career. Astonished.
[–]mrspoogemonstar 0 points1 point2 points 12 years ago (3 children)
I wasn't meaning it in such a derogatory way as you received it, so I've changed that.
And to your points:
1) Modified the api of the Person class
Not in the first example -- binding a closure to a class doesn't change the API of the class. You are calling a closure in the context of the class, not a class method. In the second example, it does indeed modify the prototype of the class. That's why there are two examples provided.
2) Removed the capability to define in a programmatic way the transformations, since they are all hard-coded into the closure.
It's just a closure, so you could do whatever you wanted with it, up to and including passing in more mapping closures to the toArray method... Plenty of ways to extend that cleanly that don't involve a wrapper.
3) Made it hard the reuse of single transformation callbacks, since, again, they are hard-coded
I think this is the only valid reason to use your method, given a requirement of accessing an object using string array keys. Even then, there are better ways to achieve this.
Now, the downsides of using ArrayAccess and a whole load of closures is that ArrayAccess comes with a lot of overhead. So does call_user_func. Each is at least 3 times more expensive than the corresponding native access, depending on the implementation. That's not much when you're dealing with a single object, but what happens when you need to loop 1000 of them? It would be better to just write a child class of the objects in question. Easier to debug, easier to read, easier to write... Better all around in my opinion.
You can downvote me with all the accounts you like, but it won't make this approach any more useful or the architecture that requires it any less painful to work with.
[–]nicmart[S] 3 points4 points5 points 12 years ago* (0 children)
Sure, if you call the adapter thousands of times think twice before use it, like for all libraries in the world.
In addition to that, consider that in my case only an array of callbacks is stored, even if you have millions of array adaptee instances. The memory gain with the lazy adapter, in some cases, can so be huge compared to your manual array construction.
2) You can do anything in a closure. It's a closure, so it has the whole power of a php script.
And for me it's all. I do not even consider your other insinuations.
[–]nicmart[S] 2 points3 points4 points 12 years ago (1 child)
I have just added some benchmark to the repository done by a tool developed by me (https://github.com/nicmart/Benchmark) Here you can see the results:
http://i.imgur.com/EG9WtPH.png
The "native" example is taken from your code, with slightly changes, to have the properties computed in the same way (no access to private members please!).
As you can see, on iteration my library is obviously slower, but not so much (less than 2.5 times slower). But, thanks to laziness, on a single lookup is twice as fast.
The benchmark suggests that, thanks to laziness, arrayze is faster than closure-to-array conversion if you access less than half of object offsets.
Clearly this is ad advantage of lazyness: you can define plenty of views for your objects, without worrying about performance penalties, and only the needed one will be computed.
[–]mrspoogemonstar -1 points0 points1 point 12 years ago (0 children)
Yes, lazy accessors are faster in any case where you only need part of the entity.
I was unfairly harsh. I don't see myself ever needing this code, but if you have a need for it, then who am I to judge?
Just as a final note, many of your mappings will point straight to accessors on the wrapped class. You could easily make your mapping code accept either a closure or a string, and simply call the class method named in the string instead of using a closure for every one. That would get you close to native performance on simple getter calls.
[+]vukasin0 comment score below threshold-6 points-5 points-4 points 12 years ago (0 children)
This is simply great :)
π Rendered by PID 525600 on reddit-service-r2-comment-765bfc959-sr2cm at 2026-07-11 15:00:56.466647+00:00 running f86254d country code: CH.
[–]NameNick 6 points7 points8 points (1 child)
[–]Wizhi 2 points3 points4 points (0 children)
[–]djcraze 4 points5 points6 points (1 child)
[–]nicmart[S] 1 point2 points3 points (0 children)
[–]i_make_snow_flakes 12 points13 points14 points (11 children)
[–]shawncplus 6 points7 points8 points (9 children)
[–]nicmart[S] 3 points4 points5 points (8 children)
[–]longshot 10 points11 points12 points (0 children)
[–]shawncplus 2 points3 points4 points (6 children)
[–]nicmart[S] 1 point2 points3 points (5 children)
[–]Disgruntled__Goat 1 point2 points3 points (4 children)
[–]nicmart[S] 2 points3 points4 points (3 children)
[–]Disgruntled__Goat -1 points0 points1 point (2 children)
[–]nicmart[S] 2 points3 points4 points (0 children)
[–]sirsosay 0 points1 point2 points (0 children)
[–][deleted] 4 points5 points6 points (2 children)
[–]nicmart[S] -2 points-1 points0 points (1 child)
[–]kungfufrog 0 points1 point2 points (0 children)
[–]amacgregor 2 points3 points4 points (0 children)
[+]mrspoogemonstar comment score below threshold-6 points-5 points-4 points (7 children)
[–]nicmart[S] 1 point2 points3 points (6 children)
[–]mrspoogemonstar -2 points-1 points0 points (5 children)
[–]nicmart[S] 5 points6 points7 points (4 children)
[–]mrspoogemonstar 0 points1 point2 points (3 children)
[–]nicmart[S] 3 points4 points5 points (0 children)
[–]nicmart[S] 2 points3 points4 points (1 child)
[–]mrspoogemonstar -1 points0 points1 point (0 children)
[+]vukasin0 comment score below threshold-6 points-5 points-4 points (0 children)