Skip to content

Commit 494d55e

Browse files
committed
Merge remote-tracking branch 'upstream/master' into 1.1-dev
2 parents 352e1b6 + f56c77e commit 494d55e

19 files changed

+394
-30
lines changed

.editorconfig

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# editorconfig.org
2+
root = true
3+
4+
[*]
5+
indent_style = space
6+
indent_size = 2
7+
end_of_line = lf
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
insert_final_newline = true
11+
12+
[*.md]
13+
trim_trailing_whitespace = false
14+
15+
[*.php]
16+
indent_style = tab
17+
indent_size = 4
18+
19+
[*.{xml,xml.dist}]
20+
indent_size = 4

ActiveRecord.php

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
require __DIR__.'/lib/Singleton.php';
1111
require __DIR__.'/lib/Config.php';
1212
require __DIR__.'/lib/Utils.php';
13+
require __DIR__.'/lib/DateTimeInterface.php';
1314
require __DIR__.'/lib/DateTime.php';
1415
require __DIR__.'/lib/Model.php';
1516
require __DIR__.'/lib/Table.php';

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"php": ">=5.3.0"
1010
},
1111
"require-dev": {
12-
"phpunit/phpunit": "3.7.*",
12+
"phpunit/phpunit": "4.*",
1313
"pear/pear_exception": "1.0-beta1",
1414
"pear/log": "~1.12"
1515
},

lib/Column.php

+8-2
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,17 @@ public function cast($value, $connection)
169169
if (!$value)
170170
return null;
171171

172-
if ($value instanceof DateTime)
172+
$date_class = Config::instance()->get_date_class();
173+
174+
if ($value instanceof $date_class)
173175
return $value;
174176

175177
if ($value instanceof \DateTime)
176-
return new DateTime($value->format('Y-m-d H:i:s T'));
178+
return $date_class::createFromFormat(
179+
Connection::DATETIME_TRANSLATE_FORMAT,
180+
$value->format(Connection::DATETIME_TRANSLATE_FORMAT),
181+
$value->getTimezone()
182+
);
177183

178184
return $connection->string_to_datetime($value);
179185
}

lib/Config.php

+30
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ class Config extends Singleton
7272
*/
7373
private $logger;
7474

75+
/**
76+
* Contains the class name for the Date class to use. Must have a public format() method and a
77+
* public static createFromFormat($format, $time) method
78+
*
79+
* @var string
80+
*/
81+
private $date_class = 'ActiveRecord\\DateTime';
82+
7583
/**
7684
* The format to serialize DateTime values into.
7785
*
@@ -289,6 +297,28 @@ public function get_logger()
289297
return $this->logger;
290298
}
291299

300+
public function set_date_class($date_class)
301+
{
302+
try {
303+
$klass = Reflections::instance()->add($date_class)->get($date_class);
304+
} catch (\ReflectionException $e) {
305+
throw new ConfigException("Cannot find date class");
306+
}
307+
308+
if (!$klass->hasMethod('format') || !$klass->getMethod('format')->isPublic())
309+
throw new ConfigException('Given date class must have a "public format($format = null)" method');
310+
311+
if (!$klass->hasMethod('createFromFormat') || !$klass->getMethod('createFromFormat')->isPublic())
312+
throw new ConfigException('Given date class must have a "public static createFromFormat($format, $time)" method');
313+
314+
$this->date_class = $date_class;
315+
}
316+
317+
public function get_date_class()
318+
{
319+
return $this->date_class;
320+
}
321+
292322
/**
293323
* @deprecated
294324
*/

lib/Connection.php

+19-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@
2020
abstract class Connection
2121
{
2222

23+
/**
24+
* The DateTime format to use when translating other DateTime-compatible objects.
25+
*
26+
* NOTE!: The DateTime "format" used must not include a time-zone (name, abbreviation, etc) or offset.
27+
* Including one will cause PHP to ignore the passed in time-zone in the 3rd argument.
28+
* See bug: https://bugs.php.net/bug.php?id=61022
29+
*
30+
* @var string
31+
*/
32+
const DATETIME_TRANSLATE_FORMAT = 'Y-m-d\TH:i:s';
33+
2334
/**
2435
* The PDO connection object.
2536
* @var mixed
@@ -469,7 +480,7 @@ public function datetime_to_string($datetime)
469480
* Converts a string representation of a datetime into a DateTime object.
470481
*
471482
* @param string $string A datetime in the form accepted by date_create()
472-
* @return DateTime
483+
* @return object The date_class set in Config
473484
*/
474485
public function string_to_datetime($string)
475486
{
@@ -479,7 +490,13 @@ public function string_to_datetime($string)
479490
if ($errors['warning_count'] > 0 || $errors['error_count'] > 0)
480491
return null;
481492

482-
return new DateTime($date->format(static::$datetime_format));
493+
$date_class = Config::instance()->get_date_class();
494+
495+
return $date_class::createFromFormat(
496+
static::DATETIME_TRANSLATE_FORMAT,
497+
$date->format(static::DATETIME_TRANSLATE_FORMAT),
498+
$date->getTimezone()
499+
);
483500
}
484501

485502
/**

lib/DateTime.php

+62-8
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
* @package ActiveRecord
3434
* @see http://php.net/manual/en/class.datetime.php
3535
*/
36-
class DateTime extends \DateTime
36+
class DateTime extends \DateTime implements DateTimeInterface
3737
{
3838
/**
3939
* Default format used for format() and __toString()
@@ -113,11 +113,40 @@ public static function get_format($format=null)
113113
return $format;
114114
}
115115

116+
/**
117+
* This needs to be overriden so it returns an instance of this class instead of PHP's \DateTime.
118+
* See http://php.net/manual/en/datetime.createfromformat.php
119+
*/
120+
public static function createFromFormat($format, $time, $tz = null)
121+
{
122+
$phpDate = $tz ? parent::createFromFormat($format, $time, $tz) : parent::createFromFormat($format, $time);
123+
if (!$phpDate)
124+
return false;
125+
// convert to this class using the timestamp
126+
$ourDate = new static(null, $phpDate->getTimezone());
127+
$ourDate->setTimestamp($phpDate->getTimestamp());
128+
return $ourDate;
129+
}
130+
116131
public function __toString()
117132
{
118133
return $this->format();
119134
}
120135

136+
/**
137+
* Handle PHP object `clone`.
138+
*
139+
* This makes sure that the object doesn't still flag an attached model as
140+
* dirty after cloning the DateTime object and making modifications to it.
141+
*
142+
* @return void
143+
*/
144+
public function __clone()
145+
{
146+
$this->model = null;
147+
$this->attribute_name = null;
148+
}
149+
121150
private function flag_dirty()
122151
{
123152
if ($this->model)
@@ -127,24 +156,49 @@ private function flag_dirty()
127156
public function setDate($year, $month, $day)
128157
{
129158
$this->flag_dirty();
130-
call_user_func_array(array($this,'parent::setDate'),func_get_args());
159+
return parent::setDate($year, $month, $day);
131160
}
132161

133-
public function setISODate($year, $week , $day=null)
162+
public function setISODate($year, $week , $day = 1)
134163
{
135164
$this->flag_dirty();
136-
call_user_func_array(array($this,'parent::setISODate'),func_get_args());
165+
return parent::setISODate($year, $week, $day);
137166
}
138167

139-
public function setTime($hour, $minute, $second=null)
168+
public function setTime($hour, $minute, $second = 0)
140169
{
141170
$this->flag_dirty();
142-
call_user_func_array(array($this,'parent::setTime'),func_get_args());
171+
return parent::setTime($hour, $minute, $second);
143172
}
144173

145174
public function setTimestamp($unixtimestamp)
146175
{
147176
$this->flag_dirty();
148-
call_user_func_array(array($this,'parent::setTimestamp'),func_get_args());
177+
return parent::setTimestamp($unixtimestamp);
149178
}
150-
}
179+
180+
public function setTimezone($timezone)
181+
{
182+
$this->flag_dirty();
183+
return parent::setTimezone($timezone);
184+
}
185+
186+
public function modify($modify)
187+
{
188+
$this->flag_dirty();
189+
return parent::modify($modify);
190+
}
191+
192+
public function add($interval)
193+
{
194+
$this->flag_dirty();
195+
return parent::add($interval);
196+
}
197+
198+
public function sub($interval)
199+
{
200+
$this->flag_dirty();
201+
return parent::sub($interval);
202+
}
203+
204+
}

lib/DateTimeInterface.php

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
/**
3+
* @package ActiveRecord
4+
*/
5+
namespace ActiveRecord;
6+
7+
/**
8+
* Interface for the ActiveRecord\DateTime class so that ActiveRecord\Model->assign_attribute() will
9+
* know to call attribute_of() on passed values. This is so the DateTime object can flag the model
10+
* as dirty via $model->flag_dirty() when one of its setters is called.
11+
*
12+
* @package ActiveRecord
13+
* @see http://php.net/manual/en/class.datetime.php
14+
*/
15+
interface DateTimeInterface
16+
{
17+
/**
18+
* Indicates this object is an attribute of the specified model, with the given attribute name.
19+
*
20+
* @param Model $model The model this object is an attribute of
21+
* @param string $attribute_name The attribute name
22+
* @return void
23+
*/
24+
public function attribute_of($model, $attribute_name);
25+
26+
/**
27+
* Formats the DateTime to the specified format.
28+
*/
29+
public function format($format=null);
30+
31+
/**
32+
* See http://php.net/manual/en/datetime.createfromformat.php
33+
*/
34+
public static function createFromFormat($format, $time, $tz = null);
35+
}

lib/Model.php

+13-6
Original file line numberDiff line numberDiff line change
@@ -477,12 +477,19 @@ public function assign_attribute($name, $value)
477477
}
478478

479479
// convert php's \DateTime to ours
480-
if ($value instanceof \DateTime)
481-
$value = new DateTime($value->format('Y-m-d H:i:s T'));
480+
if ($value instanceof \DateTime) {
481+
$date_class = Config::instance()->get_date_class();
482+
if (!($value instanceof $date_class))
483+
$value = $date_class::createFromFormat(
484+
Connection::DATETIME_TRANSLATE_FORMAT,
485+
$value->format(Connection::DATETIME_TRANSLATE_FORMAT),
486+
$value->getTimezone()
487+
);
488+
}
482489

483-
// make sure DateTime values know what model they belong to so
484-
// dirty stuff works when calling set methods on the DateTime object
485-
if ($value instanceof DateTime)
490+
if ($value instanceof DateTimeInterface)
491+
// Tell the Date object that it's associated with this model and attribute. This is so it
492+
// has the ability to flag this model as dirty if a field in the Date object changes.
486493
$value->attribute_of($this,$name);
487494

488495
// only update the attribute if it isn't set or has changed
@@ -991,7 +998,7 @@ protected function cache_key()
991998
* Delete all using a string:
992999
*
9931000
* <code>
994-
* YourModel::delete_all(array('conditions' => 'name = "Tito"));
1001+
* YourModel::delete_all(array('conditions' => 'name = "Tito"'));
9951002
* </code>
9961003
*
9971004
* An options array takes the following parameters:

lib/Serialization.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,10 @@ final protected function options_to_a($key)
221221
*/
222222
final public function to_a()
223223
{
224+
$date_class = Config::instance()->get_date_class();
224225
foreach ($this->attributes as &$value)
225226
{
226-
if ($value instanceof \DateTime)
227+
if ($value instanceof $date_class)
227228
$value = $value->format(self::$DATETIME_FORMAT);
228229
}
229230
return $this->attributes;

lib/Table.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -428,9 +428,10 @@ private function &process_data($hash)
428428
if (!$hash)
429429
return $hash;
430430

431+
$date_class = Config::instance()->get_date_class();
431432
foreach ($hash as $name => &$value)
432433
{
433-
if ($value instanceof \DateTime)
434+
if ($value instanceof $date_class || $value instanceof \DateTime)
434435
{
435436
if (isset($this->columns[$name]) && $this->columns[$name]->type == Column::DATE)
436437
$hash[$name] = $this->conn->date_to_string($value);
@@ -518,7 +519,7 @@ private function set_associations()
518519
foreach (wrap_strings_in_arrays($definitions) as $definition)
519520
{
520521
$relationship = null;
521-
$definition += compact('namespace');
522+
$definition += array('namespace' => $namespace);
522523

523524
switch ($name)
524525
{

lib/Utils.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ public static function is_blank($var)
241241
'/(bu)s$/i' => "$1ses",
242242
'/(alias)$/i' => "$1es",
243243
'/(octop)us$/i' => "$1i",
244-
'/(ax|test)is$/i' => "$1es",
244+
'/(cris|ax|test)is$/i' => "$1es",
245245
'/(us)$/i' => "$1es",
246246
'/s$/i' => "s",
247247
'/$/' => "s"

lib/Validations.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ public function validates_format_of($attrs)
442442
$attribute = $options[0];
443443
$var = $this->model->$attribute;
444444

445-
if (is_null($options['with']) || !is_string($options['with']) || !is_string($options['with']))
445+
if (is_null($options['with']) || !is_string($options['with']))
446446
throw new ValidationsArgumentError('A regular expression must be supplied as the [with] option of the configuration array.');
447447
else
448448
$expression = $options['with'];
@@ -600,7 +600,7 @@ public function validates_uniqueness_of($attrs)
600600
continue;
601601

602602
$pk = $this->model->get_primary_key();
603-
$pk_value = $this->model->$pk[0];
603+
$pk_value = $this->model->{$pk[0]};
604604

605605
if (is_array($options[0]))
606606
{
@@ -979,4 +979,4 @@ public function getIterator()
979979
{
980980
return new ArrayIterator($this->full_messages());
981981
}
982-
}
982+
}

0 commit comments

Comments
 (0)