1<?php
2/**
3 * PHPTAL templating engine
4 *
5 * PHP Version 5
6 *
7 * @category HTML
8 * @package  PHPTAL
9 * @author   Laurent Bedubourg <lbedubourg@motion-twin.com>
10 * @author   Kornel Lesiński <kornel@aardvarkmedia.co.uk>
11 * @author   Iván Montes <drslump@pollinimini.net>
12 * @license  http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
13 * @version  SVN: $Id$
14 * @link     http://phptal.org/
15 */
16
17/**
18 * Keeps track of variable contents when using grouping in a path (first/ and last/)
19 *
20 * @package PHPTAL
21 * @subpackage Php
22 */
23class PHPTAL_RepeatControllerGroups
24{
25    protected $dict = array();
26    protected $cache = array();
27    protected $data = null;
28    protected $vars = array();
29    protected $branch;
30
31
32    public function __construct()
33    {
34        $this->dict = array();
35        $this->reset();
36    }
37
38    /**
39     * Resets the result caches. Use it to signal an iteration in the loop
40     *
41     */
42    public function reset()
43    {
44        $this->cache = array();
45    }
46
47    /**
48     * Checks if the data passed is the first one in a group
49     *
50     * @param mixed $data   The data to evaluate
51     *
52     * @return Mixed    True if the first item in the group, false if not and
53     *                  this same object if the path is not finished
54     */
55    public function first($data)
56    {
57        if ( !is_array($data) && !is_object($data) && !is_null($data) ) {
58
59            if ( !isset($this->cache['F']) ) {
60
61                $hash = md5($data);
62
63                if ( !isset($this->dict['F']) || $this->dict['F'] !== $hash ) {
64                    $this->dict['F'] = $hash;
65                    $res = true;
66                } else {
67                    $res = false;
68                }
69
70                $this->cache['F'] = $res;
71            }
72
73            return $this->cache['F'];
74        }
75
76        $this->data = $data;
77        $this->branch = 'F';
78        $this->vars = array();
79        return $this;
80    }
81
82    /**
83     * Checks if the data passed is the last one in a group
84     *
85     * @param mixed $data   The data to evaluate
86     *
87     * @return Mixed    True if the last item in the group, false if not and
88     *                  this same object if the path is not finished
89     */
90    public function last($data)
91    {
92        if ( !is_array($data) && !is_object($data) && !is_null($data) ) {
93
94            if ( !isset($this->cache['L']) ) {
95
96                $hash = md5($data);
97
98                if (empty($this->dict['L'])) {
99                    $this->dict['L'] = $hash;
100                    $res = false;
101                } elseif ($this->dict['L'] !== $hash) {
102                    $this->dict['L'] = $hash;
103                    $res = true;
104                } else {
105                    $res = false;
106                }
107
108                $this->cache['L'] = $res;
109            }
110
111            return $this->cache['L'];
112        }
113
114        $this->data = $data;
115        $this->branch = 'L';
116        $this->vars = array();
117        return $this;
118    }
119
120    /**
121     * Handles variable accesses for the tal path resolver
122     *
123     * @param string $var   The variable name to check
124     *
125     * @return Mixed    An object/array if the path is not over or a boolean
126     *
127     * @todo    replace the PHPTAL_Context::path() with custom code
128     */
129    public function __get($var)
130    {
131        // When the iterator item is empty we just let the tal
132        // expression consume by continuously returning this
133        // same object which should evaluate to true for 'last'
134        if ( is_null($this->data) ) {
135            return $this;
136        }
137
138        // Find the requested variable
139        $value = PHPTAL_Context::path($this->data, $var, true);
140
141        // Check if it's an object or an array
142        if ( is_array($value) || is_object($value) ) {
143            // Move the context to the requested variable and return
144            $this->data = $value;
145            $this->addVarName($var);
146            return $this;
147        }
148
149        // get a hash of the variable contents
150        $hash = md5($value);
151
152        // compute a path for the variable to use as dictionary key
153        $path = $this->branch . $this->getVarPath() . $var;
154
155        // If we don't know about this var store in the dictionary
156        if ( !isset($this->cache[$path]) ) {
157
158            if ( !isset($this->dict[$path]) ) {
159                $this->dict[$path] = $hash;
160                $res = $this->branch === 'F';
161            } else {
162                // Check if the value has changed
163                if ($this->dict[$path] !== $hash) {
164                    $this->dict[$path] = $hash;
165                    $res = true;
166                } else {
167                    $res = false;
168                }
169            }
170
171            $this->cache[$path] = $res;
172        }
173
174        return $this->cache[$path];
175
176    }
177
178    /**
179     * Adds a variable name to the current path of variables
180     *
181     * @param string $varname  The variable name to store as a path part
182     * @access protected
183     */
184    protected function addVarName($varname)
185    {
186        $this->vars[] = $varname;
187    }
188
189    /**
190     * Returns the current variable path separated by a slash
191     *
192     * @return String  The current variable path
193     * @access protected
194     */
195    protected function getVarPath()
196    {
197        return implode('/', $this->vars) . '/';
198    }
199}
200