PageRenderTime 42ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/phocoa/framework/WFArray.php

https://github.com/SwissalpS/phocoa
PHP | 266 lines | 208 code | 11 blank | 47 comment | 25 complexity | f5047770d1e2205cc5002d86d9f36a82 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. /**
  4. * @package KeyValueCoding
  5. * @copyright Copyright (c) 2005 Alan Pinstein. All Rights Reserved.
  6. * @version $Id: kvcoding.php,v 1.3 2004/12/12 02:44:09 alanpinstein Exp $
  7. * @author Alan Pinstein <apinstein@mac.com>
  8. */
  9. /**
  10. * A KVC-compliant array wrapper.
  11. *
  12. * To get the values of the array you can iterate on it or just call {@link WFArray::values()}.
  13. *
  14. * @todo Make completely KVC: setValueForKey, setValueForKeyPath, valuesForKeys, valuesForKeyPaths
  15. * What should stuff like valueForUndefinedKey, validateValueForKey, etc do? nothing?
  16. */
  17. class WFArray extends ArrayObject
  18. {
  19. /**
  20. * A convenience function to create a hash of the contained values in interesting ways.
  21. *
  22. * @param string The key to use on each array entry to generate the hash key for the entry. NULL used sequential numerical indices rather than a hash key.
  23. * @param mixed
  24. * NULL => the entry for each key will be the entire entry in the array
  25. * string => the entry for each key will be the valueForKeyPath() of the passed key path
  26. * array => the entry for each key will be an associative array, the result of passing the argument to valuesForKeyPaths()
  27. * @return array An associative array of the contained values hashed according to the parameters provided.
  28. */
  29. public function hash($hashKey, $keyPath = NULL)
  30. {
  31. if (!is_null($keyPath) and !is_string($keyPath) and !is_array($keyPath))
  32. {
  33. throw new WFException("KeyPath must be NULL, a string, or an array of strings.");
  34. }
  35. $hash = array();
  36. foreach ($this as $entry) {
  37. // calculate $hashInfo, which will be the value pointed to by the key
  38. if ($keyPath === NULL)
  39. {
  40. $hashInfo = $entry;
  41. }
  42. else if ($entry instanceof WFObject && is_string($keyPath))
  43. {
  44. $hashInfo = $entry->valueForKeyPath($keyPath);
  45. }
  46. else if ($entry instanceof WFObject && is_array($keyPath))
  47. {
  48. $hashInfo = $entry->valuesForKeyPaths($keyPath);
  49. }
  50. else if (is_array($entry) && is_string($keyPath))
  51. {
  52. $hashInfo = $entry[$keyPath];
  53. }
  54. else if (is_array($entry) && is_array($keyPath))
  55. {
  56. $hashInfo = array();
  57. foreach ($keyPath as $p)
  58. {
  59. $hashInfo[$p] = $entry[$p];
  60. }
  61. }
  62. else
  63. {
  64. throw new WFException("This array contained an entry that wasn't a WFObject or Array.");
  65. }
  66. // calculate the hash key used to reference the calculated $hasInfo value above
  67. // add the calculated value to our output hash
  68. if ($entry instanceof WFObject && $hashKey)
  69. {
  70. $hash[$entry->valueForKey($hashKey)] = $hashInfo;
  71. }
  72. else if (is_array($entry) && $hashKey)
  73. {
  74. $hash[$entry[$hashKey]] = $hashInfo;
  75. }
  76. else
  77. {
  78. $hash[] = $hashInfo;
  79. }
  80. }
  81. return $hash;
  82. }
  83. /**
  84. * A convenience function to create a WFArray of WFArrays keyed by the valueForKeyPath of each object.
  85. *
  86. * Whereas hash assumes that the hash key produces UNIQUE results, chunk assumes that the hash key produces mutliple results.
  87. *
  88. * Example:
  89. * [
  90. * {
  91. * id: 1,
  92. * category: foo
  93. * },
  94. * {
  95. * id: 2,
  96. * category: foo
  97. * },
  98. * {
  99. * id: 3,
  100. * category: bar
  101. * },
  102. * {
  103. * id: 4,
  104. * category: bar
  105. * }
  106. * ]
  107. *
  108. * WFArray::arrayWithArray($example)->chunk('category')
  109. *
  110. * {
  111. * foo: [obj1, obj2],
  112. * bar: [obj3, obj4]
  113. * }
  114. *
  115. * @param string The key to use on each array entry to generate the hash key for the entry.
  116. * @return array An associative array of the contained values hashed according to the parameters provided. All items whose valueForKeyPath match will be included in the chunk.
  117. */
  118. public function chunk($hashKey)
  119. {
  120. $chunked = new WFArray();
  121. foreach ($this as $entry)
  122. {
  123. $key = $entry->valueForKeyPath($hashKey);
  124. if (!isset($chunked[$key]))
  125. {
  126. $chunked[$key] = new WFArray();
  127. }
  128. $chunked[$key][] = $entry;
  129. }
  130. return $chunked;
  131. }
  132. /**
  133. * Canonical map function.
  134. *
  135. * The map function is a wrapper around {@link hash()}; the map argument can thus do some magical things since it is invoked via {@link WFObject::valueForKeyPath()}.
  136. *
  137. * @param mixed
  138. * NULL => the entry for each key will be the entire entry in the array
  139. * string => the entry for each key will be the valueForKey() of the passed key
  140. * array => the entry for each key will be an associative array, the result of passing the argument to valuesForKeyPaths()
  141. * @return array An associative array of the contained values hashed according to the parameters provided.
  142. */
  143. public function map($keyPath)
  144. {
  145. if (is_null($keyPath)) throw new WFException("Cannot call map with NULL argument. I think you're looking for WFArray::values().");
  146. return $this->hash(NULL, $keyPath);
  147. }
  148. /**
  149. * Get a subset of the array for the passed set of keys.
  150. *
  151. * @param array An array of keys to look for in the array.
  152. * @return object WFArray A new associative array with all key/value pairs matching the passed-in set of keys.
  153. */
  154. public function subArrayWithKeys($keys)
  155. {
  156. $matchedArray = array();
  157. foreach ($keys as $key) {
  158. if (array_key_exists($key, $this))
  159. {
  160. $matchedArray[$key] = $this[$key];
  161. }
  162. }
  163. return new WFArray($matchedArray);
  164. }
  165. /**
  166. * Get all of the values contained in the array
  167. *
  168. * NOTE: calls getArrayCopy.
  169. *
  170. * @return array
  171. */
  172. public function values()
  173. {
  174. return $this->getArrayCopy();
  175. }
  176. public function valueForKey($key)
  177. {
  178. if ($key === 'values')
  179. {
  180. return $this->values();
  181. }
  182. else if ($this->offsetExists($key))
  183. {
  184. $v = $this[$key];
  185. if (is_array($v) and !($v instanceof WFArray))
  186. {
  187. return new WFArray($v);
  188. }
  189. return $v;
  190. }
  191. else if (method_exists($this, $key))
  192. {
  193. return $this->$key();
  194. }
  195. else
  196. {
  197. throw new WFUndefinedKeyException("No value exists for key {$key}. \$this is a WFArray; did you mean 'values.{$key}'?");
  198. }
  199. }
  200. public function setValueForKey($value, $key)
  201. {
  202. $this[$key] = $value;
  203. }
  204. public function setValuesForKeys($valuesForKeys)
  205. {
  206. foreach ($valuesForKeys as $k => $v) {
  207. $this->setValueForKey($v, $k);
  208. }
  209. }
  210. public function valueForKeyPath($keyPath)
  211. {
  212. return WFObject::valueForTargetAndKeyPath($keyPath, $this);
  213. }
  214. // @todo Factor out into WFKVC::valuesForKeyPaths($keysAndKeyPaths, $object)
  215. public function valuesForKeyPaths($keysAndKeyPaths)
  216. {
  217. $hash = array();
  218. // fix integer keys into keys... this allows keysAndKeyPaths to return ('myProp', 'myProp2' => 'myKeyPath', 'myProp3')
  219. foreach ( array_keys($keysAndKeyPaths) as $k ) {
  220. if (gettype($k) == 'integer')
  221. {
  222. $keysAndKeyPaths[$keysAndKeyPaths[$k]] = $keysAndKeyPaths[$k];
  223. unset($keysAndKeyPaths[$k]);
  224. }
  225. }
  226. foreach ($keysAndKeyPaths as $k => $keyPath) {
  227. $v = $this->valueForKeyPath($keyPath);
  228. $hash[$k] = $v;
  229. }
  230. return $hash;
  231. }
  232. /**
  233. * Helper static initializer to create a new array for fluent interfaces.
  234. *
  235. * @param array
  236. * @return object WFArray
  237. */
  238. public static function arrayWithArray($array)
  239. {
  240. return new WFArray($array);
  241. }
  242. public function __toString()
  243. {
  244. $str = "Array:\n";
  245. foreach ($this as $k => $v) {
  246. $str .= " {$k} => {$v}\n";
  247. }
  248. $str .= "END Array\n";
  249. return $str;
  250. }
  251. }