PageRenderTime 57ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/context.php

http://github.com/m3talsmith/php-liquid
PHP | 326 lines | 136 code | 82 blank | 108 comment | 42 complexity | 0610463dfd86c42ca78546d2347ee2e7 MD5 | raw file
  1. <?php
  2. /**
  3. * Liquid for PHP
  4. *
  5. * @package Liquid
  6. * @copyright Copyright (c) 2006 Mateo Murphy,
  7. * based on Liquid for Ruby (c) 2006 Tobias Luetke
  8. * @license http://www.opensource.org/licenses/mit-license.php
  9. *
  10. */
  11. /**
  12. * Context keeps the variable stack and resolves variables, as well as keywords
  13. *
  14. * @package Liquid
  15. */
  16. class LiquidContext {
  17. /**
  18. * Local scopes
  19. *
  20. * @var array
  21. */
  22. var $assigns;
  23. /**
  24. * Registers for non-variable state data
  25. *
  26. * @var array
  27. */
  28. var $registers;
  29. /**
  30. * The filterbank holds all the filters
  31. *
  32. * @var LiquidFilterbank
  33. */
  34. var $filterbank;
  35. /**
  36. * Constructor
  37. *
  38. * @param array $assigns
  39. * @param array $registers
  40. * @return LiquidContext
  41. */
  42. function LiquidContext($assigns = null, $registers = null) {
  43. if (isset($assigns)) {
  44. $this->assigns = array($assigns);
  45. } else {
  46. $this->assigns = array(array());
  47. }
  48. if (isset($registers)) {
  49. $this->registers = $registers;
  50. } else {
  51. $this->registers = array();
  52. }
  53. $this->filterbank = new LiquidFilterbank($this);
  54. }
  55. /**
  56. * Add a filter to the context
  57. *
  58. * @param mixed $filter
  59. */
  60. function add_filters($filter) {
  61. $this->filterbank->add_filter($filter);
  62. }
  63. /**
  64. * Invoke the filter that matches given name
  65. *
  66. * @param string $name The name of the filter
  67. * @param mixed $value The value to filter
  68. * @param array $args Additional arguments for the filter
  69. * @return string
  70. */
  71. function invoke($name, $value, $args = null) {
  72. return $this->filterbank->invoke($name, $value, $args);
  73. }
  74. /**
  75. * Merges the given assigns into the current assigns
  76. *
  77. * @param array $new_assigns
  78. */
  79. function merge($new_assigns) {
  80. $this->assigns[0] = array_merge($this->assigns[0], $new_assigns);
  81. }
  82. /**
  83. * Push new local scope on the stack.
  84. *
  85. * @return bool
  86. */
  87. function push() {
  88. array_unshift($this->assigns, array());
  89. return true;
  90. }
  91. /**
  92. * Pops the current scope from the stack.
  93. *
  94. * @return bool
  95. */
  96. function pop() {
  97. if (count($this->assigns) == 1) {
  98. trigger_error('No elements to pop', E_USER_ERROR);
  99. return false;
  100. }
  101. array_shift($this->assigns);
  102. }
  103. /**
  104. * Replaces []
  105. *
  106. * @param string
  107. * @return mixed
  108. */
  109. function get($key) {
  110. return $this->resolve($key);
  111. }
  112. /**
  113. * Replaces []=
  114. *
  115. * @param string $key
  116. * @param mixed $value
  117. */
  118. function set($key, $value) {
  119. $this->assigns[0][$key] = $value;
  120. }
  121. /**
  122. * Returns true if the given key will properly resolve
  123. *
  124. * @param string $key
  125. * @return bool
  126. */
  127. function has_key($key) {
  128. return (!is_null($this->resolve($key)));
  129. }
  130. /**
  131. * Resolve a key by either returning the appropriate literal or by looking up the appropriate variable
  132. *
  133. * Test for empty has been moved to interpret condition, in LiquidDecisionBlock
  134. *
  135. * @param string $key
  136. * @return mixed
  137. */
  138. function resolve($key) {
  139. // this shouldn't happen
  140. if (is_array($key)) {
  141. trigger_error("Cannot resolve arrays as key", E_USER_ERROR);
  142. return null;
  143. }
  144. if (is_null($key) || $key == 'null') {
  145. return null;
  146. }
  147. if ($key == 'true') {
  148. return true;
  149. }
  150. if ($key == 'false') {
  151. return false;
  152. }
  153. if (preg_match('/^\'(.*)\'$/', $key, $matches)) {
  154. return $matches[1];
  155. }
  156. if (preg_match('/^"(.*)"$/', $key, $matches)) {
  157. return $matches[1];
  158. }
  159. if (preg_match('/^(\d+)$/', $key, $matches)) {
  160. return $matches[1];
  161. }
  162. if (preg_match('/^(\d[\d\.]+)$/', $key, $matches)) {
  163. return $matches[1];
  164. }
  165. return $this->variable($key);
  166. }
  167. /**
  168. * Fetches the current key in all the scopes
  169. *
  170. * @param string $key
  171. * @return mixed
  172. */
  173. function fetch($key) {
  174. foreach ($this->assigns as $scope) {
  175. if (array_key_exists($key, $scope)) {
  176. $obj = $scope[$key];
  177. if (is_a($obj, 'LiquidDrop')) {
  178. $obj->context = $this;
  179. }
  180. return $obj;
  181. }
  182. }
  183. }
  184. /**
  185. * Resolved the namespaced queries gracefully.
  186. *
  187. * @param string $key
  188. * @return mixed
  189. */
  190. function variable($key) {
  191. $parts = explode(LIQUID_VARIABLE_ATTRIBUTE_SEPERATOR, $key);
  192. $object = $this->fetch(array_shift($parts));
  193. if (is_object($object)) {
  194. $object = $object->to_liquid();
  195. }
  196. if (!is_null($object)) {
  197. while (count($parts) > 0) {
  198. if (is_a($object, 'LiquidDrop')) {
  199. $object->context = $this;
  200. }
  201. $next_part_name = array_shift($parts);
  202. if (is_array($object)) {
  203. // if the last part of the context variable is .size we just return the count
  204. if ($next_part_name == 'size' && count($parts) == 0 && !array_key_exists('size', $object)) {
  205. return count($object);
  206. }
  207. if (array_key_exists($next_part_name, $object)) {
  208. $object = $object[$next_part_name];
  209. } else {
  210. return null;
  211. }
  212. } else if (is_object($object)) {
  213. if (is_a($object, 'LiquidDrop')) {
  214. // if the object is a drop, make sure it supports the given method
  215. if (!$object->has_key($next_part_name)) {
  216. return null;
  217. }
  218. // php4 doesn't support array access, so we have
  219. // to use the invoke method instead
  220. $object = $object->invoke_drop($next_part_name);
  221. } elseif(method_exists($object, LIQUID_HAS_PROPERTY_METHOD)) {
  222. if (!call_user_method(LIQUID_HAS_PROPERTY_METHOD, $object, $next_part_name)) {
  223. return null;
  224. }
  225. $object = call_user_method(LIQUID_GET_PROPERTY_METHOD, $object, $next_part_name);
  226. } else {
  227. // if it's just a regular object, attempt to access a property
  228. if (!property_exists($object, $next_part_name)) {
  229. return null;
  230. }
  231. $object = $object->$next_part_name;
  232. }
  233. }
  234. if (is_object($object) && method_exists($object, 'to_liquid')) {
  235. $object = $object->to_liquid();
  236. }
  237. }
  238. return $object;
  239. } else {
  240. return null;
  241. }
  242. }
  243. }
  244. ?>