|
|
|
#1 |
|
Messages: n/a
Hébergeur: |
This question should probably be directed at the PHP Internals list, but
I thought I would start by asking here first if anybody would even use this feature. It has been brought to my attention that with Perl and Ruby, you can use Objects as the value of the key within an array. The examples that were shown to me make me think that this would be an awesome ability to have within PHP. Here is an example of how I would use such a feature. I have a table of "customers". I have a table customers "contact_locations". This table has a reference ID back to the "customers" table. Now, in my array of information that I build I have this. $res = query('Select c_id, c_first_name, c_last_name FROM customers'); $customers = array(); while ( $row = fetch_assoc($res) ) { $customers[$row]['contact_locations'] = array(); $SQL = "SELECT * FROM contact_locations WHERE c_id={$row['c_id']}"; $loc_res = query($SQL); while ( $loc_row = fetch_assoc($loc_res) ) { $customers[$row]['contact_locations'][] = $loc_row; } } Now, contained within one array "$customers" I have all the information that would be needed for displaying any type of information related to a customer or a customers location. By doing having this feature, I could build the ability to do xPath searches within the base array. That would be nice. Anyways, what do you all think? Worth it or not? Jim Lucas |
|
|
|
#2 |
|
Messages: n/a
Hébergeur: |
Jim Lucas wrote:
> This question should probably be directed at the PHP Internals list, but > I thought I would start by asking here first if anybody would even use > this feature. > > It has been brought to my attention that with Perl and Ruby, you can use > Objects as the value of the key within an array. The examples that were > shown to me make me think that this would be an awesome ability to have > within PHP. > > Here is an example of how I would use such a feature. > > I have a table of "customers". > I have a table customers "contact_locations". > This table has a reference ID back to the "customers" table. > > Now, in my array of information that I build I have this. > > $res = query('Select c_id, c_first_name, c_last_name FROM customers'); > > $customers = array(); > while ( $row = fetch_assoc($res) ) { > $customers[$row]['contact_locations'] = array(); > $SQL = "SELECT * > FROM contact_locations > WHERE c_id={$row['c_id']}"; > $loc_res = query($SQL); > while ( $loc_row = fetch_assoc($loc_res) ) { > $customers[$row]['contact_locations'][] = $loc_row; > } > } > > > Now, contained within one array "$customers" I have all the information > that would be needed for displaying any type of information related to a > customer or a customers location. > > By doing having this feature, I could build the ability to do xPath > searches within the base array. That would be nice. > > Anyways, what do you all think? Worth it or not? > > Jim Lucas > Maybe I don't get the new concept, but what would be wrong with: // START CODE // $cid = null; $customers = array(); while ( $row = fetch_assoc($res) ) { $cid = $row['cid']; $customers[$cid] = $row; $customers[$cid]['contact_locations'] = array(); $SQL = "SELECT * FROM contact_locations WHERE c_id={$cid}"; $loc_res = query($SQL); while ( $loc_row = fetch_assoc($loc_res) ) { $customers[$cid]['contact_locations'][] = $loc_row; } } // END CODE // Now you can easily reference all of your customers by ID and get any of the data you want. I would actually just load the record I want from the database and not chew up all that memory, but this is just an example anyway. I guess it could allow you to somewhat consolidate a two-dimensional array down to a single dimension. In order to use the data, you'd have to use array_keys() or something similar to use it though. It's an interesting idea, but I don't know how practical it would be. -- Ray Hauge www.primateapplications.com |
|
|
|
#3 |
|
Messages: n/a
Hébergeur: |
On Mar 15, 2008, at 7:16 PM, Ray Hauge wrote:
> Jim Lucas wrote: >> It has been brought to my attention that with Perl and Ruby, you >> can use Objects as the value of the key within an array. The >> examples that were shown to me make me think that this would be an >> awesome ability to have within PHP. >> $res = query('Select c_id, c_first_name, c_last_name FROM >> customers'); >> $customers = array(); >> while ( $row = fetch_assoc($res) ) { >> $customers[$row]['contact_locations'] = array(); >> $SQL = "SELECT * >> FROM contact_locations >> WHERE c_id={$row['c_id']}"; >> $loc_res = query($SQL); >> while ( $loc_row = fetch_assoc($loc_res) ) { >> $customers[$row]['contact_locations'][] = $loc_row; >> } >> } >> Now, contained within one array "$customers" I have all the >> information that would be needed for displaying any type of >> information related to a customer or a customers location. >> You may have meant the while loop to iterate over $row = fetch_object($res) ... > // START CODE // > $cid = null; > $customers = array(); > > while ( $row = fetch_assoc($res) ) { > > $cid = $row['cid']; > $customers[$cid] = $row; > $customers[$cid]['contact_locations'] = array(); > > $SQL = "SELECT * > FROM contact_locations > WHERE c_id={$cid}"; > > $loc_res = query($SQL); > > while ( $loc_row = fetch_assoc($loc_res) ) { > $customers[$cid]['contact_locations'][] = $loc_row; > } > > } > // END CODE // You could also create a data object associated with customer that makes use of overloading functions -- or whatever you kids are calling them these days to allow: $customer = new Customer(78); // Load customer with ID 78. $customer->loadLocations(); // Loads related locations. $customer->location[n]; // Returns nth location. $customer->location('home'); // Returns the location dubbed "home." // You can have location loadLocations() if they're not already set. The data object would have to make use of __get, __set, and __call. Hereby, the locations aren't indexed by the object, but are elements in a member array. |
|
|
|
#4 |
|
Messages: n/a
Hébergeur: |
On Sat, 2008-03-15 at 15:02 -0700, Jim Lucas wrote: > This question should probably be directed at the PHP Internals list, but > I thought I would start by asking here first if anybody would even use > this feature. > > It has been brought to my attention that with Perl and Ruby, you can use > Objects as the value of the key within an array. The examples that were > shown to me make me think that this would be an awesome ability to have > within PHP. > > Here is an example of how I would use such a feature. > > I have a table of "customers". > I have a table customers "contact_locations". > This table has a reference ID back to the "customers" table. > > Now, in my array of information that I build I have this. > > $res = query('Select c_id, c_first_name, c_last_name FROM customers'); > > $customers = array(); > while ( $row = fetch_assoc($res) ) { > $customers[$row]['contact_locations'] = array(); > $SQL = "SELECT * > FROM contact_locations > WHERE c_id={$row['c_id']}"; > $loc_res = query($SQL); > while ( $loc_row = fetch_assoc($loc_res) ) { > $customers[$row]['contact_locations'][] = $loc_row; > } > } > > > Now, contained within one array "$customers" I have all the information > that would be needed for displaying any type of information related to a > customer or a customers location. Specifically for the example above, I'd only use 2 queries to link up the contact locations to the customer: <?php $sql = 'SELECT ' .' c_id, ' .' c_first_name, ' .' c_last_name ' .'FROM ' .' customers '; $customers = array(); if( ($res = some_db_query( $sql )) ) { while( ($row = some_db_fetch_assoc( $res )) ) { $row['contact_locations'] = array(); $customers[$row['c_id']] = $row; } } $customer_ids = implode( ',', array_keys( $customers ) ); $sql = 'SELECT ' .' * ' .'FROM ' .' contact_locations ' .'WHERE ' .' c_id IN ( '.$customer_ids.' ) '; if( ($res = some_db_query( $sql )) ) { while( ($row = some_db_fetch_assoc( $res )) ) { $customers[$row['c_id']]['contact_locations'][] = $row; } } ?> Imagine 200 customers in your example... your example will hit the DB 201 times. The above hits the DB twice. > By doing having this feature, I could build the ability to do xPath > searches within the base array. That would be nice. > > Anyways, what do you all think? Worth it or not? I don't see what added advantage you get by having an object or array() as a key. Why can't you have the contact_locations as a key in the $row? Cheers, Rob. -- http://www.interjinn.com Application and Templating Framework for PHP |
|
|
|
#5 |
|
Messages: n/a
Hébergeur: |
Hi,
Going off the subject alone, you might want to investigate the __tostring() magic method that you can define to handle objects being cast to a string. Also, there's a whole bunch of magic methods that you can use to handle PHP operations on user defined objects: http://uk.php.net/manual/en/language.oop5.magic.php -- Richard Heyes Employ me: http://www.phpguru.org/cv |
|
|
|
#6 |
|
Messages: n/a
Hébergeur: |
On Sat, Mar 15, 2008 at 6:02 PM, Jim Lucas <lists@cmsws.com> wrote:
> It has been brought to my attention that with Perl and Ruby, you can use > Objects as the value of the key within an array. The examples that were > shown to me make me think that this would be an awesome ability to have > within PHP. this is essentially already possible, w/ some limitations due to type checking w/in php. remember that spl thing that everyone loves to bash? well thats what makes this remotely possible. you can put elements in using array notation and objects as keys, but thats about as far as it goes. some of the global functions like array_key_exists() dont work because they check the type of the $needle argument internally. also, iteration via foreach does not work correctly. implementing the Iterator interface to tell php how to iterate over the structure works as expected however a strange error is raised when trying to retrieve the current key: PHP Warning: Illegal type returned from ObjectArray::key(). LAME, *sigh*. anyway, its still pretty cool, and part of what youre looking for. i might see if marcus could tell me something about a condition w/in the code for key() inside of php that would allow this to actually work, because imho, if its overridden the internal check thats currently in place should be bypassed. also forgive the long post (theres code to follow )<?php class ObjectArray implements ArrayAccess, Countable, Iterator{ private $indexes = array(); private $values = array(); private $curOffset = -1; private $totalElements = 0; /// Countable interface public function count() { return $this->totalElements; } /// ArrayAccess interface public function offsetExists($offset) { $offsetExists = false; foreach($this->indexes as $curIndex => $curObj) { if($curObj === $offset) { $offsetExists = true; $this->curOffset = $curIndex; break; } } return $offsetExists; } public function offsetGet($offset) { if($this->offsetExists($offset)) { return $this->values[$this->curOffset]; } } public function offsetSet($offset, $value) { if(!$this->offsetExists($offset)) { // grab index of offset /// set value of offset for first time $this->indexes[] = $offset; $this->values[] = $value; $this->totalElements++; } else { /// update value of offset $this->values[$this->curOffset] = $value; } } public function offsetUnset($offset) { if($this->offsetExists($offset)) { unset($this->indexes[$this->curOffset]); unset($this->values[$this->curOffset]); $this->totalElements--; } } /// Iterator interface public function current() { return $this->values[$this->curOffset]; } public function key() { return $this->indexes[$this->curOffset]; } public function next() { $this->curOffset++; } public function rewind() { $this->curOffset = 0; } public function valid() { $isValid = false; if(isset($this->indexes[$this->curOffset]) && isset($this->values[$this->curOffset])) { $isValid = true; } return $isValid; } } $objArr = new ObjectArray(); /// make $a & $c the same so == comparison should succeed $a = new StdClass(); $b = new StdClass(); $b->a = 'spl is the shit ';$c = new stdClass(); // test putting a value in w/ object as index $objArr[$a] = 5; echo $objArr[$a] . PHP_EOL; // test changing the value $objArr[$a] = 6; echo $objArr[$a] . PHP_EOL; // note we can put in objs that pass == test, because internally // we use === $objArr[$c] = 7; echo 'count: ' . count($objArr) . PHP_EOL; $objArr[$b] = 8; echo 'count: ' . count($objArr) . PHP_EOL; var_dump($objArr[$a] == $objArr[$b]); // false var_dump($objArr[$a] == $objArr[$c]); // false /// broken even w/ Iterator implemented ![]() foreach($objArr as $key => $val) { if($a === $key) { echo "keyFound: {$objArr[$a]}" . PHP_EOL; break; } } -nathan |
|
|
|
#7 |
|
Messages: n/a
Hébergeur: |
after talking w/ marcus it sounds like theyve explicitly decided not to
support objects in the aforementioned context. -nathan |
|
|
|
#8 |
|
Messages: n/a
Hébergeur: |
Robert Cummings wrote:
> Imagine 200 customers in your example... your example will hit the DB > 201 times. The above hits the DB twice. My code was intended to get the concept across. Thanks for the suggestions though. |
|
|
|
#9 |
|
Messages: n/a
Hébergeur: |
Richard Heyes wrote:
> Hi, > > Going off the subject alone, you might want to investigate the > __tostring() magic method that you can define to handle objects being > cast to a string. Also, there's a whole bunch of magic methods that you > can use to handle PHP operations on user defined objects: > > http://uk.php.net/manual/en/language.oop5.magic.php > I think I understand where you are going with your response, but still I don't see how incorporating __toString() into the equation would . I would still loose reference to the original object data. Jim |
|
|
|
#10 |
|
Messages: n/a
Hébergeur: |
On Sun, 2008-03-16 at 11:42 -0700, Jim Lucas wrote: > Robert Cummings wrote: > > Imagine 200 customers in your example... your example will hit the DB > > 201 times. The above hits the DB twice. > > My code was intended to get the concept across. Thanks for the > suggestions though. Ah, ok. I've inherited projects in the past where the kind of code you showed was defacto... except the number of queries made to render a homepage wasn't 201 it was thousands. In one case someone built a category pullout menu that was generating close to 18000 queries. That holds my record for inefficient DB queries. Cheers, Rob. -- http://www.interjinn.com Application and Templating Framework for PHP |
|
|
|
#11 |
|
Messages: n/a
Hébergeur: |
Robert Cummings wrote:
> On Sun, 2008-03-16 at 11:42 -0700, Jim Lucas wrote: >> Robert Cummings wrote: >>> Imagine 200 customers in your example... your example will hit the DB >>> 201 times. The above hits the DB twice. >> My code was intended to get the concept across. Thanks for the >> suggestions though. > > Ah, ok. I've inherited projects in the past where the kind of code you > showed was defacto... except the number of queries made to render a > homepage wasn't 201 it was thousands. In one case someone built a > category pullout menu that was generating close to 18000 queries. That > holds my record for inefficient DB queries. > > Cheers, > Rob. That beats my example. My current project at work, rebuilding the billing system, topped at about 6000 queries in one page. But, that page could vary by the number of services/locations a given customer has, the large the number of services/locations, the larger the number of queries. Right now, one customer has about 3500 services in 5 different locations. I have been able to pair the number of queries down to a dozen or so. Not sure what the previous developer was thinking.... The DB is much happier! Jim |
|
|
|
#12 |
|
Messages: n/a
Hébergeur: |
Sorry for the long post, but I thought it would explain much of my
thinking... Anyway, here we go... Jeremy Mcentire wrote: > You may have meant the while loop to iterate over $row = > fetch_object($res) ... You are correct, I was simply trying to get the concept across. But you are correct, I should have used *_object() instead. The basic idea is to always end up with an array of objects. > > You could also create a data object associated with customer that makes > use of overloading functions -- or whatever you kids are calling them > these days to allow: > > $customer = new Customer(78); // Load customer with ID 78. > $customer->loadLocations(); // Loads related locations. > $customer->location[n]; // Returns nth location. > $customer->location('home'); // Returns the location dubbed "home." > // You can have location loadLocations() if they're not already set. > This is how I currently do things. I have customer objects, contact objects, location objects, service objects, attribute objects, etc... and they all have similar getXXX() methods. # Returns me all customers $customers = new Customers(); $customers->getCustomers(); echo $customers; // __toString() returns the total number of customers # Returns me a specific customer, or creates a new/empty customer object $customer = new Customer([ int $customer_id ]); $customer->getLocations(); // Returns locations objects echo $customer; // __toString() returns the name of the customer # Returns an object that will allow me to get all customer locations $locations = new Locations( int $customer_id ); $locations->getLocations(); // Returns array of location objects echo $locations; // __toString() returns the total number of locations # Returns a location object, or creates a new/empty location object $location = new Location([ int $location_id ]); $location->getServices(); // Returns array of service objects $location->getCustomer(); // Returns customer object echo $location; // __toString() returns the name of the location # Returns an object that contains all services to a given location $services = new Services( int $location_id ); $services->getServices(); // Returns array of service objects echo $services; // __toString() returns the total number of services # Returns a service object, or creates a new/empty service object $service = new Service([ int $service_id ]); $service->getAttributes(); // Returns array of attribute objects $service->getLocation(); // Returns location object echo $service; // __toString() returns the name of the service # Returns an object that contains all service attributes $attributes = new Attributes( int $service_id ); $attributes->getAttributes(); // Returns array of attribute objects echo $attributes; // __toString() returns the total number of attributes # Returns a service object, or creates a new/empty service object $attribute = new Attribute([ int $attribute_id ]); echo $attribute; // __toString() returns the value of the attribute I have many other types of objects: logs, CDR's, tasks, tickets, etc... > The data object would have to make use of __get, __set, and __call. Each of the above objects use these functions and cleans data being extracted from and validates data being pushed into the said object. The objects contains regex, string length, number range, and other types of checks before the data is accepted into the object. > > Hereby, the locations aren't indexed by the object, but are elements in > a member array. Correct! And this member array/object is only created upon request. Therefor the overhead of creating said array/object is only felt when needed. Jim Lucas |
|
|
|
#13 |
|
Messages: n/a
Hébergeur: |
another developer brought to my attention the spl method spl_object_hash()
method which afforded a mod in the previously posted class whereby foreach could be used properly. also, it allows a more efficient internal implementation. however, to my dismay, it appears that implementing ArrayAccess does not (not just in this scenario, but in any) allow the class which does so to hook into the global array methods such as array_keys() or array_key_exists(). in this sense, i see it as being only about halfway as useful as it could (or should) be. what i see ArrayAccess in php as, is something quite similar to properties in vb.net, if anyones familiar w/ that. http://www.vbdotnetheaven.com/Upload...nVbDotNet.aspx this is the second scenario where i think there could be c code in php itself that would override standard behavior provided an spl definition in user space. sure, the internal code would run a bit slower but imagine wrapping it in #ifdef directives and mapping those to a configure flag whereby if spl wasnt enabled that code wasnt included. i think that would be adequate and it would allow spl to be even more powerful, giving php programmers the ability to more dramatically, semantically modify the core w/o writing a scrap of low-level code. if anybody cares, im happy to send the code for the revision of ObjectArray that allows usage of the foreach construct over instances of it. -nathan |
|
|
|
#14 |
|
Messages: n/a
Hébergeur: |
Nathan Nobbe wrote:
> another developer brought to my attention the spl method spl_object_hash() > method which afforded a mod in the previously posted class whereby foreach > could be used properly. also, it allows a more efficient internal > implementation. however, to my dismay, it appears that implementing > ArrayAccess does not (not just in this scenario, but in any) allow the class > which does so to hook into the global array methods such as array_keys() or > array_key_exists(). in this sense, i see it as being only about halfway as > useful as it could (or should) be. > what i see ArrayAccess in php as, is something quite similar to properties > in vb.net, if anyones familiar w/ that. > http://www.vbdotnetheaven.com/Upload...nVbDotNet.aspx > > this is the second scenario where i think there could be c code in php > itself that would override standard behavior provided an spl definition in > user space. sure, the internal code would run a bit slower but imagine > wrapping it in #ifdef directives and mapping those to a configure flag > whereby if spl wasnt enabled that code wasnt included. i think that would > be adequate and it would allow spl to be even more powerful, giving php > programmers the ability to more dramatically, semantically modify the core > w/o writing a scrap of low-level code. > > if anybody cares, im happy to send the code for the revision of ObjectArray > that allows usage of the foreach construct over instances of it. > > -nathan > I would be interested in your examples. From what you described, I can't see in my head how it all goes together. Thanks -- Jim Lucas "Some men are born to greatness, some achieve greatness, and some have greatness thrust upon them." Twelfth Night, Act II, Scene V by William Shakespeare |
|
|
|
#15 |
|
Messages: n/a
Hébergeur: |
On Wed, Mar 19, 2008 at 10:37 AM, Jim Lucas <lists@cmsws.com> wrote:
> I would be interested in your examples. From what you described, I can't > see in > my head how it all goes together. > there was one caveat; i had to introduce a keyVal() instance method so client code can get the object that is the current array 'key' because internally it is the hash from spl_object_hash(), which is a string. using this hash cut down on a number of loops that were in the old implementation so i suspect this one is a little faster to boot. <?php class ObjectArray implements ArrayAccess, Countable, Iterator{ private $offsets = array(); // for optimizing iterator implementation private $indexes = array(); // array of object hashes private $values = array(); // array of mixed values private $curOffset = ''; private $curOffsetNum = -1; private $totalElements = 0; /// Countable interface public function count() { return $this->totalElements; } /** * a function to grab the actual value of the key, * if needed */ public function keyVal($key=null) { if(!is_null($key)) { return $this->indexes[$key]; } else { return $this->indexes[$this->key()]; } } /// ArrayAccess interface public function offsetExists($offset) { $offsetExists = false; $hash = spl_object_hash($offset); if(isset($this->indexes[$hash]) && isset($this->values[$hash])) { $offsetExists = true; $this->curOffset = $hash; } return $offsetExists; } public function offsetGet($offset) { if($this->offsetExists($offset)) { return $this->values[$this->curOffset]; } } /** * @note at this point it is assumed that $offset is an object */ public function offsetSet($offset, $value) { if(!$this->offsetExists($offset)) { // grab index of offset /// set value of offset for first time $hash = spl_object_hash($offset); $this->indexes[$hash] = $offset; $this->values[$hash] = $value; $this->offsets[] = $hash; $this->totalElements++; } else { /// update value of offset $this->values[$this->curOffset] = $value; } } public function offsetUnset($offset) { if($this->offsetExists($offset)) { unset($this->indexes[$this->curOffset]); unset($this->values[$this->curOffset]); $this->totalElements--; } } /// Iterator interface public function current() { return $this->values[$this->key()]; } public function key() { return $this->offsets[$this->curOffsetNum]; } public function next() { $this->curOffsetNum++; } public function rewind() { if($this->totalElements > 0) { $this->curOffsetNum = 0; } else { $this->curOffsetNum = -1; } } public function valid() { $isValid = false; if(isset($this->indexes[$this->offsets[$this->curOffsetNum]]) && isset($this->values[$this->key()])) { $isValid = true; } return $isValid; } } $objArr = new ObjectArray(); /// make $a & $c the same so == comparison should succeed $a = new StdClass(); $b = new StdClass(); $b->a = 'spl is the shit ';$c = new stdClass(); // test putting a value in w/ object as index $objArr[$a] = 5; echo $objArr[$a] . PHP_EOL; // test changing the value $objArr[$a] = 6; echo $objArr[$a] . PHP_EOL; // note we can put in objs that pass == test, because internally // we use === $objArr[$c] = 7; echo 'count: ' . count($objArr) . PHP_EOL; $objArr[$b] = 8; echo 'count: ' . count($objArr) . PHP_EOL; var_dump($objArr[$a] == $objArr[$b]); // false var_dump($objArr[$a] == $objArr[$c]); // false /// now working thanks to spl_object_hash() foreach($objArr as $key => $val) { if($a === $objArr->keyVal()) { echo "keyFound: {$objArr[$a]}" . PHP_EOL; break; } } ?> -nathan |
|
![]() |
| Outils de la discussion | |
|
|