1 | <?php |
||
2 | |||
3 | /** |
||
4 | * @file ViewHandler.php |
||
5 | * @brief This file contains the ViewHandler class. |
||
6 | * @details |
||
7 | * @author Filippo F. Fadda |
||
8 | */ |
||
9 | |||
10 | |||
11 | namespace EoC\Handler; |
||
12 | |||
13 | |||
14 | use Lint\Lint; |
||
15 | |||
16 | |||
17 | /** |
||
18 | * @brief This handler let you create a CouchDB view. |
||
19 | * @details Views are the primary tool used for querying and reporting on CouchDB databases. Views are managed by a |
||
20 | * special server. Default server implementation uses JavaScript, that's why you have to write views in JavaScript |
||
21 | * language. This handler instead let you write your views directly in PHP.\n |
||
22 | * If you have specified 'php' as your design document language, this handler makes a syntax check on your map and |
||
23 | * reduce functions.\n |
||
24 | * To create a permanent view, map and reduce functions must first be saved into special design document. Every design |
||
25 | * document has a special `views` attribute, that stores mandatory map function and an optional reduce function. Using |
||
26 | * this handler you can write these functions directly in PHP.\n |
||
27 | * All the views in one design document are indexed whenever any of them gets queried. |
||
28 | * @nosubgrouping |
||
29 | * |
||
30 | * @cond HIDDEN_SYMBOLS |
||
31 | * |
||
32 | * @property string $name // The view handler name. |
||
33 | * @property string $language // The programming language used to write map and reduce functions. |
||
34 | * @property string $mapFn // Stores the map function. |
||
35 | * @property string $reduceFn // Stores the reduce function. |
||
36 | * |
||
37 | * @endcond |
||
38 | * |
||
39 | * @todo: Add support for seq_indexed option. |
||
40 | */ |
||
41 | final class ViewHandler extends DesignHandler { |
||
42 | const MAP_REGEX = '/function\s*\(\s*\$doc\)\s*use\s*\(\$emit\)\s*\{[\W\w]*\};\z/m'; |
||
43 | const MAP_DEFINITION = "function(\$doc) use (\$emit) { ... };"; |
||
44 | |||
45 | const REDUCE_REGEX = '/function\s*\(\s*\$keys\s*,\s*\$values\,\s*\$rereduce\)\s*\{[\W\w]*\};\z/m'; |
||
46 | const REDUCE_DEFINITION = "function(\$keys, \$values, \$rereduce) { ... };"; |
||
47 | |||
48 | private $options = []; |
||
49 | |||
50 | /** @name Properties */ |
||
51 | //!@{ |
||
0 ignored issues
–
show
|
|||
52 | |||
53 | //! The view handler name. |
||
54 | private $name; |
||
55 | |||
56 | /** |
||
57 | * @brief The programming language used to write map and reduce functions. |
||
58 | * @details This property is used mainly by the Couch::queryTempView() method. In fact, the language is taken from |
||
59 | * the design document where your map and reduce functions have been stored. |
||
60 | */ |
||
61 | private $language = ""; |
||
62 | |||
63 | /** |
||
64 | * @brief Stores the map function. |
||
65 | * @details Contains the function implementation provided by the user. You can have multiple views in a design document |
||
66 | * and for every single view you can have only one map function. The map function is a closure. |
||
67 | * The closure must be declared like: |
||
68 | @code |
||
69 | function($doc) use ($emit) { |
||
70 | ... |
||
71 | |||
72 | $emit($key, $value); |
||
73 | }; |
||
74 | @endcode |
||
75 | * To emit your record you must call the `$emit` closure. |
||
76 | */ |
||
77 | private $mapFn = ""; |
||
78 | |||
79 | /** |
||
80 | * @brief Stores the reduce function. |
||
81 | * @details Contains the function implementation provided by the user. You can have multiple views in a design document |
||
82 | * and for every single view you can have only one reduce function. The reduce function is a closure. |
||
83 | * The closure must be declared like: |
||
84 | @code |
||
85 | function($keys, $values, $rereduce) { |
||
86 | ... |
||
87 | |||
88 | }; |
||
89 | @endcode |
||
90 | */ |
||
91 | private $reduceFn = ""; |
||
92 | |||
93 | //!@} |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
100% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
94 | |||
95 | |||
96 | /** |
||
97 | * @brief Creates a ViewHandler class instance. |
||
98 | * @param[in] string $name Handler name. |
||
99 | * @param[in] string $language (optional) The map/reduce functions' language. |
||
100 | */ |
||
101 | public function __construct($name, $language = "php") { |
||
102 | $this->setName($name); |
||
103 | $this->setLanguage($language); |
||
104 | } |
||
105 | |||
106 | |||
107 | //! @cond HIDDEN_SYMBOLS |
||
108 | |||
109 | public function getName() { |
||
110 | return $this->name; |
||
111 | } |
||
112 | |||
113 | |||
114 | public function setName($value) { |
||
115 | $this->name = (string)$value; |
||
116 | } |
||
117 | |||
118 | |||
119 | public function setLanguage($value) { |
||
120 | $this->language = (string)$value; |
||
121 | } |
||
122 | |||
123 | |||
124 | public static function getSection() { |
||
125 | return 'views'; |
||
126 | } |
||
127 | |||
128 | //! @endcond |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
50% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
129 | |||
130 | |||
131 | /** |
||
132 | * @brief Resets the options. |
||
133 | */ |
||
134 | public function reset() { |
||
135 | unset($this->options); |
||
136 | $this->options = []; |
||
137 | |||
138 | $this->mapFn = ""; |
||
139 | $this->reduceFn = ""; |
||
140 | } |
||
141 | |||
142 | |||
143 | public function isConsistent() { |
||
144 | return (!empty($this->name) && !empty($this->mapFn)) ? TRUE : FALSE; |
||
145 | } |
||
146 | |||
147 | |||
148 | /** |
||
149 | * @brief Checks the function definition against a regular expression and use PHP lint to find syntax errors. |
||
150 | * @param[in] string $fnImpl The function's implementation. |
||
151 | * @param[in] string $fnDef The function prototype. |
||
152 | * @param[in] string $fnRegex The regular expression to check the function correctness. |
||
153 | */ |
||
154 | public static function checkFn($fnImpl, $fnDef, $fnRegex) { |
||
155 | Lint::checkSourceCode($fnImpl); |
||
156 | |||
157 | if (!preg_match($fnRegex, $fnImpl)) |
||
158 | throw new \Exception("The \$closure must be defined like: $fnDef"); |
||
159 | } |
||
160 | |||
161 | |||
162 | public function asArray() { |
||
163 | $view = []; |
||
164 | $view['map'] = $this->mapFn; |
||
165 | |||
166 | if (!empty($this->language)) |
||
167 | $view['language'] = $this->language; |
||
168 | |||
169 | if (!empty($this->reduceFn)) |
||
170 | $view['reduce'] = $this->reduceFn; |
||
171 | |||
172 | if (!empty($this->options)) |
||
173 | $view['options'] = $this->options; |
||
174 | |||
175 | return $view; |
||
176 | } |
||
177 | |||
178 | |||
179 | /** |
||
180 | * @brief Makes documents' local sequence numbers available to map functions as a '_local_seq' document property. |
||
181 | */ |
||
182 | public function includeLocalSeq() { |
||
183 | $this->options['local_seq'] = 'true'; |
||
184 | } |
||
185 | |||
186 | |||
187 | /** |
||
188 | * @brief Causes map functions to be called on design documents as well as regular documents. |
||
189 | */ |
||
190 | public function includeDesignDocs() { |
||
191 | $this->options['include_design'] = 'true'; |
||
192 | } |
||
193 | |||
194 | |||
195 | /** |
||
196 | * @brief Sets the reduce function to the built-in `_count` function provided by CouchDB. |
||
197 | * @details The built-in `_count` reduce function will be probably the most common reduce function you'll use. |
||
198 | * This function returns the number of mapped values in the set. |
||
199 | */ |
||
200 | public function useBuiltInReduceFnCount() { |
||
201 | $this->reduceFn = "_count"; |
||
202 | } |
||
203 | |||
204 | |||
205 | /** |
||
206 | * @brief Sets the reduce function to the built-in `_sum` function provided by CouchDB. |
||
207 | * @details The built-in `_sum` reduce function will return a sum of mapped values. As with all reductions, you |
||
208 | * can either get a sum of all values grouped by keys or part of keys. You can control this behaviour when you query |
||
209 | * the view, using an instance of ViewQueryOpts class, in particular with methods ViewQueryOpts::groupResults() and |
||
210 | * ViewQueryOpts::setGroupLevel(). |
||
211 | * @warning The built-in `_sum` reduce function requires all mapped values to be numbers. |
||
212 | */ |
||
213 | public function useBuiltInReduceFnSum() { |
||
214 | $this->reduceFn = "_sum"; |
||
215 | } |
||
216 | |||
217 | |||
218 | /** |
||
219 | * @brief Sets the reduce function to the built-in `_stats` function provided by CouchDB. |
||
220 | * @details The built-in `_stats` reduce function returns an associative array containing the sum, count, minimum, |
||
221 | * maximum, and sum over all square roots of mapped values. |
||
222 | */ |
||
223 | public function useBuiltInReduceFnStats() { |
||
224 | $this->reduceFn = "_stats"; |
||
225 | } |
||
226 | |||
227 | |||
228 | //! @cond HIDDEN_SYMBOLS |
||
229 | |||
230 | public function getMapFn() { |
||
231 | return $this->mapFn; |
||
232 | } |
||
233 | |||
234 | |||
235 | public function setMapFn($value) { |
||
236 | $fn = stripslashes((string)$value); |
||
237 | |||
238 | if ($this->language == "php") |
||
239 | self::checkFn($fn, self::MAP_DEFINITION, self::MAP_REGEX); |
||
240 | |||
241 | $this->mapFn = $fn; |
||
242 | } |
||
243 | |||
244 | |||
245 | public function getReduceFn() { |
||
246 | return $this->reduceFn; |
||
247 | } |
||
248 | |||
249 | |||
250 | public function setReduceFn($value) { |
||
251 | $fn = stripslashes((string)$value); |
||
252 | |||
253 | if ($this->language == "php") |
||
254 | self::checkFn($fn, self::REDUCE_DEFINITION, self::REDUCE_REGEX); |
||
255 | |||
256 | $this->reduceFn = $fn; |
||
257 | } |
||
258 | |||
259 | //! @endcond |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
50% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
260 | |||
261 | } |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.