SHOW:
|
|
- or go back to the newest paste.
1 | - | <?php |
1 | + | <?php |
2 | - | /** |
2 | + | /** |
3 | - | * This class layers support for plural specs such as changes, jobs, |
3 | + | * This class layers support for plural specs such as changes, jobs, |
4 | - | * users, etc. on top of the singular spec support already present |
4 | + | * users, etc. on top of the singular spec support already present |
5 | - | * in P4\Spec\SingularAbstract. |
5 | + | * in P4\Spec\SingularAbstract. |
6 | - | * |
6 | + | * |
7 | - | * @copyright 2011 Perforce Software. All rights reserved. |
7 | + | * @copyright 2011 Perforce Software. All rights reserved. |
8 | - | * @license Please see LICENSE.txt in top-level folder of this distribution. |
8 | + | * @license Please see LICENSE.txt in top-level folder of this distribution. |
9 | - | * @version <release>/<patch> |
9 | + | * @version <release>/<patch> |
10 | - | */ |
10 | + | */ |
11 | - | |
11 | + | |
12 | - | namespace P4\Spec; |
12 | + | namespace P4\Spec; |
13 | - | |
13 | + | |
14 | - | use P4; |
14 | + | use P4; |
15 | - | use P4\Validate; |
15 | + | use P4\Validate; |
16 | - | use P4\Spec\Exception\Exception; |
16 | + | use P4\Spec\Exception\Exception; |
17 | - | use P4\Spec\Exception\NotFoundException; |
17 | + | use P4\Spec\Exception\NotFoundException; |
18 | - | use P4\Connection\ConnectionInterface; |
18 | + | use P4\Connection\ConnectionInterface; |
19 | - | use P4\Model\Fielded\Iterator as FieldedIterator; |
19 | + | use P4\Model\Fielded\Iterator as FieldedIterator; |
20 | - | use P4\OutputHandler\Limit; |
20 | + | use P4\OutputHandler\Limit; |
21 | - | |
21 | + | |
22 | - | abstract class PluralAbstract extends SingularAbstract |
22 | + | abstract class PluralAbstract extends SingularAbstract |
23 | - | { |
23 | + | { |
24 | - | const ID_FIELD = null; |
24 | + | const ID_FIELD = null; |
25 | - | const FETCH_MAXIMUM = 'maximum'; |
25 | + | const FETCH_MAXIMUM = 'maximum'; |
26 | - | const FETCH_AFTER = 'after'; |
26 | + | const FETCH_AFTER = 'after'; |
27 | - | const TEMP_ID_PREFIX = '~tmp'; |
27 | + | const TEMP_ID_PREFIX = '~tmp'; |
28 | - | const TEMP_ID_DELIMITER = "."; |
28 | + | const TEMP_ID_DELIMITER = "."; |
29 | - | |
29 | + | |
30 | - | /** |
30 | + | /** |
31 | - | * Get the id of this spec entry. |
31 | + | * Get the id of this spec entry. |
32 | - | * |
32 | + | * |
33 | - | * @return null|string the id of this entry. |
33 | + | * @return null|string the id of this entry. |
34 | - | */ |
34 | + | */ |
35 | - | public function getId() |
35 | + | public function getId() |
36 | - | { |
36 | + | { |
37 | - | if (array_key_exists(static::ID_FIELD, $this->values)) { |
37 | + | if (array_key_exists(static::ID_FIELD, $this->values)) { |
38 | - | return $this->values[static::ID_FIELD]; |
38 | + | return $this->values[static::ID_FIELD]; |
39 | - | } else { |
39 | + | } else { |
40 | - | return null; |
40 | + | return null; |
41 | - | } |
41 | + | } |
42 | - | } |
42 | + | } |
43 | - | |
43 | + | |
44 | - | /** |
44 | + | /** |
45 | - | * Set the id of this spec entry. Id must be in a valid format or null. |
45 | + | * Set the id of this spec entry. Id must be in a valid format or null. |
46 | - | * |
46 | + | * |
47 | - | * @param null|string $id the id of this entry - pass null to clear. |
47 | + | * @param null|string $id the id of this entry - pass null to clear. |
48 | - | * @return PluralAbstract provides a fluent interface |
48 | + | * @return PluralAbstract provides a fluent interface |
49 | - | * @throws \InvalidArgumentException if id does not pass validation. |
49 | + | * @throws \InvalidArgumentException if id does not pass validation. |
50 | - | */ |
50 | + | */ |
51 | - | public function setId($id) |
51 | + | public function setId($id) |
52 | - | { |
52 | + | { |
53 | - | if ($id !== null && !static::isValidId($id)) { |
53 | + | if ($id !== null && !static::isValidId($id)) { |
54 | - | throw new \InvalidArgumentException("Cannot set id. Id is invalid."); |
54 | + | throw new \InvalidArgumentException("Cannot set id. Id is invalid."); |
55 | - | } |
55 | + | } |
56 | - | |
56 | + | |
57 | - | // if populate was deferred, caller expects it |
57 | + | // if populate was deferred, caller expects it |
58 | - | // to have been populated already. |
58 | + | // to have been populated already. |
59 | - | $this->populate(); |
59 | + | $this->populate(); |
60 | - | |
60 | + | |
61 | - | $this->values[static::ID_FIELD] = $id; |
61 | + | $this->values[static::ID_FIELD] = $id; |
62 | - | |
62 | + | |
63 | - | return $this; |
63 | + | return $this; |
64 | - | } |
64 | + | } |
65 | - | |
65 | + | |
66 | - | /** |
66 | + | /** |
67 | - | * Determine if a spec record with the given id exists. |
67 | + | * Determine if a spec record with the given id exists. |
68 | - | * Must be implemented by sub-classes because this test |
68 | + | * Must be implemented by sub-classes because this test |
69 | - | * is impractical to generalize. |
69 | + | * is impractical to generalize. |
70 | - | * |
70 | + | * |
71 | - | * @param string $id the id to check for. |
71 | + | * @param string $id the id to check for. |
72 | - | * @param ConnectionInterface $connection optional - a specific connection to use. |
72 | + | * @param ConnectionInterface $connection optional - a specific connection to use. |
73 | - | * @return bool true if the given id matches an existing record. |
73 | + | * @return bool true if the given id matches an existing record. |
74 | - | */ |
74 | + | */ |
75 | - | abstract public static function exists($id, ConnectionInterface $connection = null); |
75 | + | abstract public static function exists($id, ConnectionInterface $connection = null); |
76 | - | |
76 | + | |
77 | - | /** |
77 | + | /** |
78 | - | * Get the requested spec entry from Perforce. |
78 | + | * Get the requested spec entry from Perforce. |
79 | - | * |
79 | + | * |
80 | - | * @param string $id the id of the entry to fetch. |
80 | + | * @param string $id the id of the entry to fetch. |
81 | - | * @param ConnectionInterface $connection optional - a specific connection to use. |
81 | + | * @param ConnectionInterface $connection optional - a specific connection to use. |
82 | - | * @return PluralAbstract instace of the requested entry. |
82 | + | * @return PluralAbstract instace of the requested entry. |
83 | - | * @throws \InvalidArgumentException if no id is given. |
83 | + | * @throws \InvalidArgumentException if no id is given. |
84 | - | */ |
84 | + | */ |
85 | - | public static function fetch($id, ConnectionInterface $connection = null) |
85 | + | public static function fetch($id, ConnectionInterface $connection = null) |
86 | - | { |
86 | + | { |
87 | - | // ensure a valid id is provided. |
87 | + | // ensure a valid id is provided. |
88 | - | if (!static::isValidId($id)) { |
88 | + | if (!static::isValidId($id)) { |
89 | - | throw new \InvalidArgumentException("Must supply a valid id to fetch."); |
89 | + | throw new \InvalidArgumentException("Must supply a valid id to fetch."); |
90 | - | } |
90 | + | } |
91 | - | |
91 | + | |
92 | - | // if no connection given, use default. |
92 | + | // if no connection given, use default. |
93 | - | $connection = $connection ?: static::getDefaultConnection(); |
93 | + | $connection = $connection ?: static::getDefaultConnection(); |
94 | - | |
94 | + | |
95 | - | // ensure id exists. |
95 | + | // ensure id exists. |
96 | - | if (!static::exists($id, $connection)) { |
96 | + | if (!static::exists($id, $connection)) { |
97 | - | throw new NotFoundException( |
97 | + | throw new NotFoundException( |
98 | - | "Cannot fetch " . static::SPEC_TYPE . " $id. Record does not exist." |
98 | + | "Cannot fetch " . static::SPEC_TYPE . " $id. Record does not exist." |
99 | - | ); |
99 | + | ); |
100 | - | } |
100 | + | } |
101 | - | |
101 | + | |
102 | - | // construct spec instance. |
102 | + | // construct spec instance. |
103 | - | $spec = new static($connection); |
103 | + | $spec = new static($connection); |
104 | - | $spec->setId($id) |
104 | + | $spec->setId($id) |
105 | - | ->deferPopulate(); |
105 | + | ->deferPopulate(); |
106 | - | |
106 | + | |
107 | - | return $spec; |
107 | + | return $spec; |
108 | - | } |
108 | + | } |
109 | - | |
109 | + | |
110 | - | /** |
110 | + | /** |
111 | - | * Get all entries of this type from Perforce. |
111 | + | * Get all entries of this type from Perforce. |
112 | - | * |
112 | + | * |
113 | - | * @param array $options optional - array of options to augment fetch behavior. |
113 | + | * @param array $options optional - array of options to augment fetch behavior. |
114 | - | * supported options are: |
114 | + | * supported options are: |
115 | - | * |
115 | + | * |
116 | - | * FETCH_MAXIMUM - set to integer value to limit to the |
116 | + | * FETCH_MAXIMUM - set to integer value to limit to the |
117 | - | * first 'max' number of entries. |
117 | + | * first 'max' number of entries. |
118 | - | * FETCH_AFTER - set to an id _after_ which to start collecting entries |
118 | + | * FETCH_AFTER - set to an id _after_ which to start collecting entries |
119 | - | * note: entries seen before 'after' count towards max. |
119 | + | * note: entries seen before 'after' count towards max. |
120 | - | * |
120 | + | * |
121 | - | * @param ConnectionInterface $connection optional - a specific connection to use. |
121 | + | * @param ConnectionInterface $connection optional - a specific connection to use. |
122 | - | * @return FieldedIterator all records of this type. |
122 | + | * @return FieldedIterator all records of this type. |
123 | - | * @todo make limit work for depot (in a P4\Spec\Depot sub-class) |
123 | + | * @todo make limit work for depot (in a P4\Spec\Depot sub-class) |
124 | - | */ |
124 | + | */ |
125 | - | public static function fetchAll($options = array(), ConnectionInterface $connection = null) |
125 | + | public static function fetchAll($options = array(), ConnectionInterface $connection = null) |
126 | - | { |
126 | + | { |
127 | - | // if no connection given, use default. |
127 | + | // if no connection given, use default. |
128 | - | $connection = $connection ?: static::getDefaultConnection(); |
128 | + | $connection = $connection ?: static::getDefaultConnection(); |
129 | - | |
129 | + | |
130 | - | // get command to use |
130 | + | // get command to use |
131 | - | $command = static::getFetchAllCommand(); |
131 | + | $command = static::getFetchAllCommand(); |
132 | - | |
132 | + | |
133 | - | // get command flags for given fetch options. |
133 | + | // get command flags for given fetch options. |
134 | - | $flags = static::getFetchAllFlags($options); |
134 | + | $flags = static::getFetchAllFlags($options); |
135 | - | |
135 | + | |
136 | - | // fetch all specs. |
136 | + | // fetch all specs. |
137 | - | // configure a handler to enforce 'after' (skip entries up to and including 'after') |
137 | + | // configure a handler to enforce 'after' (skip entries up to and including 'after') |
138 | - | $after = isset($options[static::FETCH_AFTER]) ? $options[static::FETCH_AFTER] : null; |
138 | + | $after = isset($options[static::FETCH_AFTER]) ? $options[static::FETCH_AFTER] : null; |
139 | - | if (strlen($after)) { |
139 | + | if (strlen($after)) { |
140 | - | $idField = static::ID_FIELD; |
140 | + | $idField = static::ID_FIELD; |
141 | - | $isAfter = false; |
141 | + | $isAfter = false; |
142 | - | $handler = new Limit; |
142 | + | $handler = new Limit; |
143 | - | $handler->setFilterCallback( |
143 | + | $handler->setFilterCallback( |
144 | - | function ($data) use ($after, $idField, &$isAfter) { |
144 | + | function ($data) use ($after, $idField, &$isAfter) { |
145 | - | if ($after && !$isAfter) { |
145 | + | if ($after && !$isAfter) { |
146 | - | // id field could be upper or lower case in list output. |
146 | + | // id field could be upper or lower case in list output. |
147 | - | $id = isset($data[lcfirst($idField)]) ? $data[lcfirst($idField)] : null; |
147 | + | $id = isset($data[lcfirst($idField)]) ? $data[lcfirst($idField)] : null; |
148 | - | $id = !$id && isset($data[$idField]) ? $data[$idField] : $id; |
148 | + | $id = !$id && isset($data[$idField]) ? $data[$idField] : $id; |
149 | - | $isAfter = ($after == $id); |
149 | + | $isAfter = ($after == $id); |
150 | - | return false; |
150 | + | return false; |
151 | - | } |
151 | + | } |
152 | - | return true; |
152 | + | return true; |
153 | - | } |
153 | + | } |
154 | - | ); |
154 | + | ); |
155 | - | $result = $connection->runHandler($handler, $command, $flags); |
155 | + | $result = $connection->runHandler($handler, $command, $flags); |
156 | - | } else { |
156 | + | } else { |
157 | - | $result = $connection->run($command, $flags); |
157 | + | $result = $connection->run($command, $flags); |
158 | - | } |
158 | + | } |
159 | - | |
159 | + | |
160 | - | // expand any sequences present |
160 | + | // expand any sequences present |
161 | - | $result->expandSequences(); |
161 | + | $result->expandSequences(); |
162 | - | |
162 | + | |
163 | - | // convert result data to spec objects. |
163 | + | // convert result data to spec objects. |
164 | - | $specs = new FieldedIterator; |
164 | + | $specs = new FieldedIterator; |
165 | - | foreach ($result->getData() as $data) { |
165 | + | foreach ($result->getData() as $data) { |
166 | - | $spec = static::fromSpecListEntry($data, $flags, $connection); |
166 | + | $spec = static::fromSpecListEntry($data, $flags, $connection); |
167 | - | $specs[$spec->getId()] = $spec; |
167 | + | $specs[$spec->getId()] = $spec; |
168 | - | } |
168 | + | } |
169 | - | |
169 | + | |
170 | - | return $specs; |
170 | + | return $specs; |
171 | - | } |
171 | + | } |
172 | - | |
172 | + | |
173 | - | /** |
173 | + | /** |
174 | - | * Create a temporary entry. |
174 | + | * Create a temporary entry. |
175 | - | * |
175 | + | * |
176 | - | * The passed values can, optionally, specify the id of the temp entry. |
176 | + | * The passed values can, optionally, specify the id of the temp entry. |
177 | - | * If no id is passed in values, one will be generated following the |
177 | + | * If no id is passed in values, one will be generated following the |
178 | - | * conventions described in makeTempId(). |
178 | + | * conventions described in makeTempId(). |
179 | - | * |
179 | + | * |
180 | - | * Temp entries are deleted when the connection is closed. |
180 | + | * Temp entries are deleted when the connection is closed. |
181 | - | * |
181 | + | * |
182 | - | * @param array|null $values optional - values to set on temp entry, |
182 | + | * @param array|null $values optional - values to set on temp entry, |
183 | - | * can include ID |
183 | + | * can include ID |
184 | - | * @param function|null $cleanupCallback optional - callback to use for cleanup. |
184 | + | * @param function|null $cleanupCallback optional - callback to use for cleanup. |
185 | - | * signature is: |
185 | + | * signature is: |
186 | - | * function($entry, $defaultCallback) |
186 | + | * function($entry, $defaultCallback) |
187 | - | * @param ConnectionInterface $connection optional - a specific connection to use. |
187 | + | * @param ConnectionInterface $connection optional - a specific connection to use. |
188 | - | * @return PluralAbstract instace of the temp entry. |
188 | + | * @return PluralAbstract instace of the temp entry. |
189 | - | */ |
189 | + | */ |
190 | - | public static function makeTemp( |
190 | + | public static function makeTemp( |
191 | - | array $values = null, |
191 | + | array $values = null, |
192 | - | $cleanupCallback = null, |
192 | + | $cleanupCallback = null, |
193 | - | ConnectionInterface $connection = null |
193 | + | ConnectionInterface $connection = null |
194 | - | ) { |
194 | + | ) { |
195 | - | // normalize to array |
195 | + | // normalize to array |
196 | - | $values = $values ?: array(); |
196 | + | $values = $values ?: array(); |
197 | - | |
197 | + | |
198 | - | // generate an id if no value for our id field is present |
198 | + | // generate an id if no value for our id field is present |
199 | - | if (!isset($values[static::ID_FIELD])) { |
199 | + | if (!isset($values[static::ID_FIELD])) { |
200 | - | $values[static::ID_FIELD] = static::makeTempId(); |
200 | + | $values[static::ID_FIELD] = static::makeTempId(); |
201 | - | } |
201 | + | } |
202 | - | |
202 | + | |
203 | - | // create the temporary instance. |
203 | + | // create the temporary instance. |
204 | - | $temp = new static($connection); |
204 | + | $temp = new static($connection); |
205 | - | $temp->set($values)->save(); |
205 | + | $temp->set($values)->save(); |
206 | - | |
206 | + | |
207 | - | // remove the temp entry when the connection terminates. |
207 | + | // remove the temp entry when the connection terminates. |
208 | - | $defaultCallback = static::getTempCleanupCallback(); |
208 | + | $defaultCallback = static::getTempCleanupCallback(); |
209 | - | $temp->getConnection()->addDisconnectCallback( |
209 | + | $temp->getConnection()->addDisconnectCallback( |
210 | - | function ($connection) use ($temp, $cleanupCallback, $defaultCallback) { |
210 | + | function ($connection) use ($temp, $cleanupCallback, $defaultCallback) { |
211 | - | try { |
211 | + | try { |
212 | - | // use the passed callback if valid, fallback to the default callback |
212 | + | // use the passed callback if valid, fallback to the default callback |
213 | - | if (is_callable($cleanupCallback)) { |
213 | + | if (is_callable($cleanupCallback)) { |
214 | - | $cleanupCallback($temp, $defaultCallback); |
214 | + | $cleanupCallback($temp, $defaultCallback); |
215 | - | } else { |
215 | + | } else { |
216 | - | $defaultCallback($temp); |
216 | + | $defaultCallback($temp); |
217 | - | } |
217 | + | } |
218 | - | } catch (\Exception $e) { |
218 | + | } catch (\Exception $e) { |
219 | - | P4\Log::logException("Failed to delete temporary entry.", $e); |
219 | + | P4\Log::logException("Failed to delete temporary entry.", $e); |
220 | - | } |
220 | + | } |
221 | - | } |
221 | + | } |
222 | - | ); |
222 | + | ); |
223 | - | |
223 | + | |
224 | - | return $temp; |
224 | + | return $temp; |
225 | - | } |
225 | + | } |
226 | - | |
226 | + | |
227 | - | /** |
227 | + | /** |
228 | - | * Generate a temporary id by combining the id prefix |
228 | + | * Generate a temporary id by combining the id prefix |
229 | - | * with the current time, pid and a random uniqid(): |
229 | + | * with the current time, pid and a random uniqid(): |
230 | - | * |
230 | + | * |
231 | - | * ~tmp.<unixtime>.<pid>.<uniqid> |
231 | + | * ~tmp.<unixtime>.<pid>.<uniqid> |
232 | - | * |
232 | + | * |
233 | - | * The leading tilde ('~') places the temporary id at the end of |
233 | + | * The leading tilde ('~') places the temporary id at the end of |
234 | - | * the list. The unixtime ensures that the oldest ids will |
234 | + | * the list. The unixtime ensures that the oldest ids will |
235 | - | * appear first (among temp ids), while the pid and uniqid provide |
235 | + | * appear first (among temp ids), while the pid and uniqid provide |
236 | - | * reasonable assurance that no two ids will collide. |
236 | + | * reasonable assurance that no two ids will collide. |
237 | - | * |
237 | + | * |
238 | - | * @return string an id suitable for use with temporary specs. |
238 | + | * @return string an id suitable for use with temporary specs. |
239 | - | */ |
239 | + | */ |
240 | - | public static function makeTempId() |
240 | + | public static function makeTempId() |
241 | - | { |
241 | + | { |
242 | - | return implode( |
242 | + | return implode( |
243 | - | static::TEMP_ID_DELIMITER, |
243 | + | static::TEMP_ID_DELIMITER, |
244 | - | array( |
244 | + | array( |
245 | - | static::TEMP_ID_PREFIX, |
245 | + | static::TEMP_ID_PREFIX, |
246 | - | time(), |
246 | + | time(), |
247 | - | getmypid(), |
247 | + | getmypid(), |
248 | - | uniqid("", true) |
248 | + | uniqid("", true) |
249 | - | ) |
249 | + | ) |
250 | - | ); |
250 | + | ); |
251 | - | } |
251 | + | } |
252 | - | |
252 | + | |
253 | - | /** |
253 | + | /** |
254 | - | * Delete this spec entry. |
254 | + | * Delete this spec entry. |
255 | - | * |
255 | + | * |
256 | - | * @param array $params optional - additional flags to pass to delete |
256 | + | * @param array $params optional - additional flags to pass to delete |
257 | - | * (e.g. some specs support -f to force delete). |
257 | + | * (e.g. some specs support -f to force delete). |
258 | - | * @return PluralAbstract provides a fluent interface |
258 | + | * @return PluralAbstract provides a fluent interface |
259 | - | * @throws Exception if no id has been set. |
259 | + | * @throws Exception if no id has been set. |
260 | - | */ |
260 | + | */ |
261 | - | public function delete(array $params = null) |
261 | + | public function delete(array $params = null) |
262 | - | { |
262 | + | { |
263 | - | $id = $this->getId(); |
263 | + | $id = $this->getId(); |
264 | - | if ($id === null) { |
264 | + | if ($id === null) { |
265 | - | throw new Exception("Cannot delete. No id has been set."); |
265 | + | throw new Exception("Cannot delete. No id has been set."); |
266 | - | } |
266 | + | } |
267 | - | |
267 | + | |
268 | - | // ensure id exists. |
268 | + | // ensure id exists. |
269 | - | $connection = $this->getConnection(); |
269 | + | $connection = $this->getConnection(); |
270 | - | if (!static::exists($id, $connection)) { |
270 | + | if (!static::exists($id, $connection)) { |
271 | - | throw new NotFoundException( |
271 | + | throw new NotFoundException( |
272 | - | "Cannot delete " . static::SPEC_TYPE . " $id. Record does not exist." |
272 | + | "Cannot delete " . static::SPEC_TYPE . " $id. Record does not exist." |
273 | - | ); |
273 | + | ); |
274 | - | } |
274 | + | } |
275 | - | |
275 | + | |
276 | - | $params = array_merge((array) $params, array("-d", $id)); |
276 | + | $params = array_merge((array) $params, array("-d", $id)); |
277 | - | $result = $connection->run(static::SPEC_TYPE, $params); |
277 | + | $result = $connection->run(static::SPEC_TYPE, $params); |
278 | - | |
278 | + | |
279 | - | // should re-populate. |
279 | + | // should re-populate. |
280 | - | $this->deferPopulate(true); |
280 | + | $this->deferPopulate(true); |
281 | - | |
281 | + | |
282 | - | return $this; |
282 | + | return $this; |
283 | - | } |
283 | + | } |
284 | - | |
284 | + | |
285 | - | /** |
285 | + | /** |
286 | - | * Get a field's raw value. |
286 | + | * Get a field's raw value. |
287 | - | * Extend parent to use getId() for id field. |
287 | + | * Extend parent to use getId() for id field. |
288 | - | * |
288 | + | * |
289 | - | * @param string $field the name of the field to get the value of. |
289 | + | * @param string $field the name of the field to get the value of. |
290 | - | * @return mixed the value of the field. |
290 | + | * @return mixed the value of the field. |
291 | - | * @throws Exception if the field does not exist. |
291 | + | * @throws Exception if the field does not exist. |
292 | - | */ |
292 | + | */ |
293 | - | public function getRawValue($field) |
293 | + | public function getRawValue($field) |
294 | - | { |
294 | + | { |
295 | - | if ($field === static::ID_FIELD) { |
295 | + | if ($field === static::ID_FIELD) { |
296 | - | return $this->getId(); |
296 | + | return $this->getId(); |
297 | - | } |
297 | + | } |
298 | - | |
298 | + | |
299 | - | // call-through. |
299 | + | // call-through. |
300 | - | return parent::getRawValue($field); |
300 | + | return parent::getRawValue($field); |
301 | - | } |
301 | + | } |
302 | - | |
302 | + | |
303 | - | /** |
303 | + | /** |
304 | - | * Set a field's raw value. |
304 | + | * Set a field's raw value. |
305 | - | * Extend parent to use setId() for id field. |
305 | + | * Extend parent to use setId() for id field. |
306 | - | * |
306 | + | * |
307 | - | * @param string $field the name of the field to set the value of. |
307 | + | * @param string $field the name of the field to set the value of. |
308 | - | * @param mixed $value the value to set in the field. |
308 | + | * @param mixed $value the value to set in the field. |
309 | - | * @return SingularAbstract provides a fluent interface |
309 | + | * @return SingularAbstract provides a fluent interface |
310 | - | * @throws Exception if the field does not exist. |
310 | + | * @throws Exception if the field does not exist. |
311 | - | */ |
311 | + | */ |
312 | - | public function setRawValue($field, $value) |
312 | + | public function setRawValue($field, $value) |
313 | - | { |
313 | + | { |
314 | - | if ($field === static::ID_FIELD) { |
314 | + | if ($field === static::ID_FIELD) { |
315 | - | return $this->setId($value); |
315 | + | return $this->setId($value); |
316 | - | } |
316 | + | } |
317 | - | |
317 | + | |
318 | - | // call-through. |
318 | + | // call-through. |
319 | - | return parent::setRawValue($field, $value); |
319 | + | return parent::setRawValue($field, $value); |
320 | - | } |
320 | + | } |
321 | - | |
321 | + | |
322 | - | /** |
322 | + | /** |
323 | - | * Extended to preserve id when values are cleared. |
323 | + | * Extended to preserve id when values are cleared. |
324 | - | * Schedule populate to run when data is requested (lazy-load). |
324 | + | * Schedule populate to run when data is requested (lazy-load). |
325 | - | * |
325 | + | * |
326 | - | * @param bool $reset optionally clear instance values. |
326 | + | * @param bool $reset optionally clear instance values. |
327 | - | */ |
327 | + | */ |
328 | - | public function deferPopulate($reset = false) |
328 | + | public function deferPopulate($reset = false) |
329 | - | { |
329 | + | { |
330 | - | if ($reset) { |
330 | + | if ($reset) { |
331 | - | $id = $this->getId(); |
331 | + | $id = $this->getId(); |
332 | - | } |
332 | + | } |
333 | - | |
333 | + | |
334 | - | parent::deferPopulate($reset); |
334 | + | parent::deferPopulate($reset); |
335 | - | |
335 | + | |
336 | - | if ($reset) { |
336 | + | if ($reset) { |
337 | - | $this->setId($id); |
337 | + | $this->setId($id); |
338 | - | } |
338 | + | } |
339 | - | } |
339 | + | } |
340 | - | |
340 | + | |
341 | - | /** |
341 | + | /** |
342 | - | * Provide a callback function to be used during cleanup of |
342 | + | * Provide a callback function to be used during cleanup of |
343 | - | * temp entries. The callback should expect a single parameter, |
343 | + | * temp entries. The callback should expect a single parameter, |
344 | - | * the entry being removed. |
344 | + | * the entry being removed. |
345 | - | * |
345 | + | * |
346 | - | * @return callable A callback function with the signature function($entry) |
346 | + | * @return callable A callback function with the signature function($entry) |
347 | - | */ |
347 | + | */ |
348 | - | protected static function getTempCleanupCallback() |
348 | + | protected static function getTempCleanupCallback() |
349 | - | { |
349 | + | { |
350 | - | return function ($entry) { |
350 | + | return function ($entry) { |
351 | - | // remove the temp entry we are responsible for |
351 | + | // remove the temp entry we are responsible for |
352 | - | $entry->delete(); |
352 | + | $entry->delete(); |
353 | - | }; |
353 | + | }; |
354 | - | } |
354 | + | } |
355 | - | |
355 | + | |
356 | - | /** |
356 | + | /** |
357 | - | * Check if the given id is in a valid format for this spec type. |
357 | + | * Check if the given id is in a valid format for this spec type. |
358 | - | * |
358 | + | * |
359 | - | * @param string $id the id to check |
359 | + | * @param string $id the id to check |
360 | - | * @return bool true if id is valid, false otherwise |
360 | + | * @return bool true if id is valid, false otherwise |
361 | - | */ |
361 | + | */ |
362 | - | protected static function isValidId($id) |
362 | + | protected static function isValidId($id) |
363 | - | { |
363 | + | { |
364 | - | $validator = new Validate\SpecName; |
364 | + | $validator = new Validate\SpecName; |
365 | - | return $validator->isValid($id); |
365 | + | return $validator->isValid($id); |
366 | - | } |
366 | + | } |
367 | - | |
367 | + | |
368 | - | /** |
368 | + | /** |
369 | - | * Extend parent populate to exit early if id is null. |
369 | + | * Extend parent populate to exit early if id is null. |
370 | - | */ |
370 | + | */ |
371 | - | protected function populate() |
371 | + | protected function populate() |
372 | - | { |
372 | + | { |
373 | - | // early exit if populate not needed. |
373 | + | // early exit if populate not needed. |
374 | - | if (!$this->needsPopulate) { |
374 | + | if (!$this->needsPopulate) { |
375 | - | return; |
375 | + | return; |
376 | - | } |
376 | + | } |
377 | - | |
377 | + | |
378 | - | // don't attempt populate if id null. |
378 | + | // don't attempt populate if id null. |
379 | - | if ($this->getId() === null) { |
379 | + | if ($this->getId() === null) { |
380 | - | return; |
380 | + | return; |
381 | - | } |
381 | + | } |
382 | - | |
382 | + | |
383 | - | parent::populate(); |
383 | + | parent::populate(); |
384 | - | } |
384 | + | } |
385 | - | |
385 | + | |
386 | - | /** |
386 | + | /** |
387 | - | * Get raw spec data direct from Perforce. No caching involved. |
387 | + | * Get raw spec data direct from Perforce. No caching involved. |
388 | - | * Extends parent to supply an id to the spec -o command. |
388 | + | * Extends parent to supply an id to the spec -o command. |
389 | - | * |
389 | + | * |
390 | - | * @return array $data the raw spec output from Perforce. |
390 | + | * @return array $data the raw spec output from Perforce. |
391 | - | */ |
391 | + | */ |
392 | - | protected function getSpecData() |
392 | + | protected function getSpecData() |
393 | - | { |
393 | + | { |
394 | - | $result = $this->getConnection()->run( |
394 | + | $result = $this->getConnection()->run( |
395 | - | static::SPEC_TYPE, |
395 | + | static::SPEC_TYPE, |
396 | - | array("-o", $this->getId()) |
396 | + | array("-o", $this->getId()) |
397 | - | ); |
397 | + | ); |
398 | - | return $result->expandSequences()->getData(-1); |
398 | + | return $result->expandSequences()->getData(-1); |
399 | - | } |
399 | + | } |
400 | - | |
400 | + | |
401 | - | /** |
401 | + | /** |
402 | - | * Given a spec entry from spec list output (e.g. 'p4 jobs'), produce |
402 | + | * Given a spec entry from spec list output (e.g. 'p4 jobs'), produce |
403 | - | * an instance of this spec with field values set where possible. |
403 | + | * an instance of this spec with field values set where possible. |
404 | - | * |
404 | + | * |
405 | - | * @param array $listEntry a single spec entry from spec list output. |
405 | + | * @param array $listEntry a single spec entry from spec list output. |
406 | - | * @param array $flags the flags that were used for this 'fetchAll' run. |
406 | + | * @param array $flags the flags that were used for this 'fetchAll' run. |
407 | - | * @param ConnectionInterface $connection a specific connection to use. |
407 | + | * @param ConnectionInterface $connection a specific connection to use. |
408 | - | * @return PluralAbstract a (partially) populated instance of this spec class. |
408 | + | * @return PluralAbstract a (partially) populated instance of this spec class. |
409 | - | */ |
409 | + | */ |
410 | - | protected static function fromSpecListEntry($listEntry, $flags, ConnectionInterface $connection) |
410 | + | protected static function fromSpecListEntry($listEntry, $flags, ConnectionInterface $connection) |
411 | - | { |
411 | + | { |
412 | - | // most spec list entries have leading lower-case field |
412 | + | // most spec list entries have leading lower-case field |
413 | - | // names which is inconsistent with defined field names. |
413 | + | // names which is inconsistent with defined field names. |
414 | - | // make all field names lead with an upper-case letter. |
414 | + | // make all field names lead with an upper-case letter. |
415 | - | $keys = array_map('ucfirst', array_keys($listEntry)); |
415 | + | $keys = array_map('ucfirst', array_keys($listEntry)); |
416 | - | $listEntry = array_combine($keys, $listEntry); |
416 | + | $listEntry = array_combine($keys, $listEntry); |
417 | - | |
417 | + | |
418 | - | // convert common timestamps to dates |
418 | + | // convert common timestamps to dates |
419 | - | if (isset($listEntry['Time'])) { |
419 | + | if (isset($listEntry['Time'])) { |
420 | - | $listEntry['Date'] = static::timeToDate($listEntry['Time'], $connection); |
420 | + | $listEntry['Date'] = static::timeToDate($listEntry['Time'], $connection); |
421 | - | unset($listEntry['Time']); |
421 | + | unset($listEntry['Time']); |
422 | - | } |
422 | + | } |
423 | - | if (isset($listEntry['Update'])) { |
423 | + | if (isset($listEntry['Update'])) { |
424 | - | $listEntry['Update'] = static::timeToDate($listEntry['Update'], $connection); |
424 | + | $listEntry['Update'] = static::timeToDate($listEntry['Update'], $connection); |
425 | - | unset($listEntry['Update']); |
425 | + | unset($listEntry['Update']); |
426 | - | } |
426 | + | } |
427 | - | if (isset($listEntry['Access'])) { |
427 | + | if (isset($listEntry['Access'])) { |
428 | - | $listEntry['Access'] = static::timeToDate($listEntry['Access'], $connection); |
428 | + | $listEntry['Access'] = static::timeToDate($listEntry['Access'], $connection); |
429 | - | unset($listEntry['Access']); |
429 | + | unset($listEntry['Access']); |
430 | - | } |
430 | + | } |
431 | - | |
431 | + | |
432 | - | // instantiate new spec object and set raw field values. |
432 | + | // instantiate new spec object and set raw field values. |
433 | - | $spec = new static($connection); |
433 | + | $spec = new static($connection); |
434 | - | $spec->setRawValues($listEntry) |
434 | + | $spec->setRawValues($listEntry) |
435 | - | ->deferPopulate(); |
435 | + | ->deferPopulate(); |
436 | - | |
436 | + | |
437 | - | return $spec; |
437 | + | return $spec; |
438 | - | } |
438 | + | } |
439 | - | |
439 | + | |
440 | - | /** |
440 | + | /** |
441 | - | * Convert the given unix timestamp into the server's typical date |
441 | + | * Convert the given unix timestamp into the server's typical date |
442 | - | * format accounting for the server's current timezone. |
442 | + | * format accounting for the server's current timezone. |
443 | - | * |
443 | + | * |
444 | - | * @param int|string $time the timestamp to convert |
444 | + | * @param int|string $time the timestamp to convert |
445 | - | * @param ConnectionInterface $connection the connection to use |
445 | + | * @param ConnectionInterface $connection the connection to use |
446 | - | * @return string date in the typical server format |
446 | + | * @return string date in the typical server format |
447 | - | */ |
447 | + | */ |
448 | - | protected static function timeToDate($time, ConnectionInterface $connection) |
448 | + | protected static function timeToDate($time, ConnectionInterface $connection) |
449 | - | { |
449 | + | { |
450 | - | $date = new \DateTime('@' . $time); |
450 | + | $date = new \DateTime('@' . $time); |
451 | - | |
451 | + | |
452 | - | // try and use the p4 info timezone, if that fails fall back to our local timezone |
452 | + | // try and use the p4 info timezone, if that fails fall back to our local timezone |
453 | - | try { |
453 | + | try { |
454 | - | $date->setTimeZone($connection->getTimeZone()); |
454 | + | $date->setTimeZone($connection->getTimeZone()); |
455 | - | } catch (\Exception $e) { |
455 | + | } catch (\Exception $e) { |
456 | - | // we tried and failed; just let it use php's default time zone |
456 | + | // we tried and failed; just let it use php's default time zone |
457 | - | } |
457 | + | // note when creating a DateTime from a unix timestamp the timezone will |
458 | - | |
458 | + | // be UTC, we need to explicitly set it to the default time zone. |
459 | - | return $date->format('Y/m/d H:i:s'); |
459 | + | $date->setTimeZone(new \DateTimeZone(date_default_timezone_get())); |
460 | - | } |
460 | + | } |
461 | - | |
461 | + | |
462 | - | /** |
462 | + | return $date->format('Y/m/d H:i:s'); |
463 | - | * Inverse function to timeToDate(), it converts the given date in server's typical |
463 | + | } |
464 | - | * format into a unix timestamp accounting for the server's current timezone. |
464 | + | |
465 | - | * |
465 | + | /** |
466 | - | * @param string $date date in typical server's format (Y/m/d H:i:s) to convert |
466 | + | * Inverse function to timeToDate(), it converts the given date in server's typical |
467 | - | * @param ConnectionInterface $connection the connection to use |
467 | + | * format into a unix timestamp accounting for the server's current timezone. |
468 | - | * @return int|false date in unix timestamp or false if unable to convert |
468 | + | * |
469 | - | */ |
469 | + | * @param string $date date in typical server's format (Y/m/d H:i:s) to convert |
470 | - | protected static function dateToTime($date, ConnectionInterface $connection) |
470 | + | * @param ConnectionInterface $connection the connection to use |
471 | - | { |
471 | + | * @return int|false date in unix timestamp or false if unable to convert |
472 | - | // try and use the p4 info timezone, if that fails fall back to our local timezone |
472 | + | */ |
473 | - | $dateTimeZone = null; |
473 | + | protected static function dateToTime($date, ConnectionInterface $connection) |
474 | - | try { |
474 | + | { |
475 | - | $dateTimeZone = $connection->getTimeZone(); |
475 | + | // try and use the p4 info timezone, if that fails fall back to our local timezone |
476 | - | } catch (\Exception $e) { |
476 | + | $dateTimeZone = null; |
477 | - | // we tried and failed; just let it use php's default time zone |
477 | + | try { |
478 | - | // note when creating a DateTime from a unix timestamp the timezone will |
478 | + | $dateTimeZone = $connection->getTimeZone(); |
479 | - | // be UTC, we need to explicitly set it to the default time zone. |
479 | + | } catch (\Exception $e) { |
480 | - | $date->setTimeZone(new \DateTimeZone(date_default_timezone_get())); |
480 | + | // we tried and failed; just let it use php's default time zone |
481 | - | } |
481 | + | } |
482 | - | |
482 | + | |
483 | - | $dateTime = $dateTimeZone |
483 | + | $dateTime = $dateTimeZone |
484 | - | ? \DateTime::createFromFormat('Y/m/d H:i:s', $date, $dateTimeZone) |
484 | + | ? \DateTime::createFromFormat('Y/m/d H:i:s', $date, $dateTimeZone) |
485 | - | : \DateTime::createFromFormat('Y/m/d H:i:s', $date); |
485 | + | : \DateTime::createFromFormat('Y/m/d H:i:s', $date); |
486 | - | |
486 | + | |
487 | - | return $dateTime ? (int) $dateTime->format('U') : false; |
487 | + | return $dateTime ? (int) $dateTime->format('U') : false; |
488 | - | } |
488 | + | } |
489 | - | |
489 | + | |
490 | - | /** |
490 | + | /** |
491 | - | * Produce set of flags for the spec list command, given fetch all options array. |
491 | + | * Produce set of flags for the spec list command, given fetch all options array. |
492 | - | * |
492 | + | * |
493 | - | * @param array $options array of options to augment fetch behavior. |
493 | + | * @param array $options array of options to augment fetch behavior. |
494 | - | * see fetchAll for documented options. |
494 | + | * see fetchAll for documented options. |
495 | - | * @return array set of flags suitable for passing to spec list command. |
495 | + | * @return array set of flags suitable for passing to spec list command. |
496 | - | */ |
496 | + | */ |
497 | - | protected static function getFetchAllFlags($options) |
497 | + | protected static function getFetchAllFlags($options) |
498 | - | { |
498 | + | { |
499 | - | $flags = array(); |
499 | + | $flags = array(); |
500 | - | |
500 | + | |
501 | - | if (isset($options[self::FETCH_MAXIMUM])) { |
501 | + | if (isset($options[self::FETCH_MAXIMUM])) { |
502 | - | $flags[] = "-m"; |
502 | + | $flags[] = "-m"; |
503 | - | $flags[] = (int) $options[self::FETCH_MAXIMUM]; |
503 | + | $flags[] = (int) $options[self::FETCH_MAXIMUM]; |
504 | - | } |
504 | + | } |
505 | - | |
505 | + | |
506 | - | return $flags; |
506 | + | return $flags; |
507 | - | } |
507 | + | } |
508 | - | |
508 | + | |
509 | - | /** |
509 | + | /** |
510 | - | * Get the fetch all command, generally a plural version of the spec type. |
510 | + | * Get the fetch all command, generally a plural version of the spec type. |
511 | - | * |
511 | + | * |
512 | - | * @return string Perforce command to use for fetchAll |
512 | + | * @return string Perforce command to use for fetchAll |
513 | - | */ |
513 | + | */ |
514 | - | protected static function getFetchAllCommand() |
514 | + | protected static function getFetchAllCommand() |
515 | - | { |
515 | + | { |
516 | - | // derive list command from spec type by adding 's' |
516 | + | // derive list command from spec type by adding 's' |
517 | - | // this works for most of the known plural specs |
517 | + | // this works for most of the known plural specs |
518 | - | return static::SPEC_TYPE . "s"; |
518 | + | return static::SPEC_TYPE . "s"; |
519 | - | } |
519 | + | } |
520 | } |