From fd56a53c88008250320404e8192e5e415f6ea763 Mon Sep 17 00:00:00 2001 From: Pankaj Kumar Date: Sun, 26 Apr 2026 00:14:39 +0530 Subject: [PATCH 1/6] Return empty string instead of null for STRING columns in cast() --- lib/Column.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Column.php b/lib/Column.php index db6c0a3..ba8517b 100644 --- a/lib/Column.php +++ b/lib/Column.php @@ -153,7 +153,7 @@ public static function castIntegerSafely($value) public function cast($value, $connection) { if ($value === null) - return null; + return $this->type === self::STRING ? '' : null; switch ($this->type) { From 8e1ae7000891ded8a20b8908790a0e1fd379abb5 Mon Sep 17 00:00:00 2001 From: filipcujanovic <29860740+filipcujanovic@users.noreply.github.com> Date: Thu, 7 May 2026 19:44:01 +0200 Subject: [PATCH 2/6] feat: update code for php version 8 --- lib/Connection.php | 2 +- lib/DateTime.php | 44 ++--- lib/Model.php | 382 +++++++++++++++++++------------------------ lib/Relationship.php | 125 ++++++-------- lib/SQLBuilder.php | 4 +- lib/Singleton.php | 6 +- lib/Utils.php | 8 +- lib/Validations.php | 115 +++++-------- 8 files changed, 295 insertions(+), 391 deletions(-) diff --git a/lib/Connection.php b/lib/Connection.php index 69e8a1b..954e14e 100644 --- a/lib/Connection.php +++ b/lib/Connection.php @@ -487,7 +487,7 @@ public function string_to_datetime($string) $date = date_create($string); $errors = \DateTime::getLastErrors(); - if ($errors['warning_count'] > 0 || $errors['error_count'] > 0) + if ($errors && ($errors['warning_count'] > 0 || $errors['error_count'] > 0)) return null; $date_class = Config::instance()->get_date_class(); diff --git a/lib/DateTime.php b/lib/DateTime.php index 43ef8a8..a9f1606 100644 --- a/lib/DateTime.php +++ b/lib/DateTime.php @@ -1,9 +1,13 @@ 'Y-m-d H:i:s', 'number' => 'YmdHis', 'time' => 'H:i', @@ -59,7 +63,8 @@ class DateTime extends \DateTime implements DateTimeInterface 'rfc2822' => \DateTime::RFC2822, 'rfc3339' => \DateTime::RFC3339, 'rss' => \DateTime::RSS, - 'w3c' => \DateTime::W3C); + 'w3c' => \DateTime::W3C + ]; private $model; private $attribute_name; @@ -84,7 +89,7 @@ public function attribute_of($model, $attribute_name) * @param string $format A format string accepted by get_format() * @return string formatted date and time string */ - public function format($format=null) + public function format(?string $format = null): string { return parent::format(self::get_format($format)); } @@ -99,7 +104,7 @@ public function format($format=null) * @param string $format A pre-defined string format or a raw format string * @return string a format string */ - public static function get_format($format=null) + public static function get_format($format = null) { // use default format if no format specified if (!$format) @@ -107,7 +112,7 @@ public static function get_format($format=null) // format is a friendly if (array_key_exists($format, self::$FORMATS)) - return self::$FORMATS[$format]; + return self::$FORMATS[$format]; // raw format return $format; @@ -117,13 +122,13 @@ public static function get_format($format=null) * This needs to be overriden so it returns an instance of this class instead of PHP's \DateTime. * See http://php.net/manual/en/datetime.createfromformat.php */ - public static function createFromFormat($format, $time, $tz = null) + public static function createFromFormat(string $format, string $time, ?DateTimeZone $tz = null): static|false { $phpDate = $tz ? parent::createFromFormat($format, $time, $tz) : parent::createFromFormat($format, $time); if (!$phpDate) return false; // convert to this class using the timestamp - $ourDate = new static(null, $phpDate->getTimezone()); + $ourDate = new static('now', $phpDate->getTimezone()); $ourDate->setTimestamp($phpDate->getTimestamp()); return $ourDate; } @@ -153,52 +158,51 @@ private function flag_dirty() $this->model->flag_dirty($this->attribute_name); } - public function setDate($year, $month, $day) + public function setDate(int $year, int $month, int $day): static { $this->flag_dirty(); return parent::setDate($year, $month, $day); } - public function setISODate($year, $week , $day = 1) + public function setISODate(int $year, int $week, int $day = 1): static { $this->flag_dirty(); return parent::setISODate($year, $week, $day); } - public function setTime($hour, $minute, $second = 0, $microseconds = 0) + public function setTime(int $hour, int $minute, int $second = 0, int $microseconds = 0): static { $this->flag_dirty(); - return parent::setTime($hour, $minute, $second); + return parent::setTime($hour, $minute, $second, $microseconds); } - public function setTimestamp($unixtimestamp) + public function setTimestamp(int $unixtimestamp): static { $this->flag_dirty(); return parent::setTimestamp($unixtimestamp); } - public function setTimezone($timezone) + public function setTimezone(\DateTimeZone $timezone): static { $this->flag_dirty(); return parent::setTimezone($timezone); } - - public function modify($modify) + + public function modify(string $modifier): static { $this->flag_dirty(); - return parent::modify($modify); + return parent::modify($modifier); } - - public function add($interval) + + public function add(\DateInterval $interval): static { $this->flag_dirty(); return parent::add($interval); } - public function sub($interval) + public function sub(\DateInterval $interval): static { $this->flag_dirty(); return parent::sub($interval); } - } diff --git a/lib/Model.php b/lib/Model.php index fbad678..eb57d37 100644 --- a/lib/Model.php +++ b/lib/Model.php @@ -1,7 +1,9 @@ __new_record = $new_record; // initialize attributes applying defaults - if (!$instantiating_via_find) - { + if (!$instantiating_via_find) { foreach (static::table()->columns as $name => $meta) $this->attributes[$meta->inflected_name] = $meta->default; } @@ -279,7 +280,7 @@ public function __construct(array $attributes=array(), $guard_attributes=true, $ if ($instantiating_via_find) $this->__dirty = array(); - $this->invoke_callback('after_construct',false); + $this->invoke_callback('after_construct', false); } /** @@ -337,8 +338,7 @@ public function __construct(array $attributes=array(), $guard_attributes=true, $ public function &__get($name) { // check for getter - if (method_exists($this, "get_$name")) - { + if (method_exists($this, "get_$name")) { $name = "get_$name"; $value = $this->$name(); return $value; @@ -355,7 +355,7 @@ public function &__get($name) */ public function __isset($attribute_name) { - return array_key_exists($attribute_name,$this->attributes) || array_key_exists($attribute_name,static::$alias_attribute); + return array_key_exists($attribute_name, $this->attributes) || array_key_exists($attribute_name, static::$alias_attribute); } /** @@ -413,25 +413,23 @@ public function __set($name, $value) if (array_key_exists($name, static::$alias_attribute)) $name = static::$alias_attribute[$name]; - elseif (method_exists($this,"set_$name")) - { + elseif (method_exists($this, "set_$name")) { $name = "set_$name"; return $this->$name($value); } - if (array_key_exists($name,$this->attributes)) - return $this->assign_attribute($name,$value); + if (array_key_exists($name, $this->attributes)) + return $this->assign_attribute($name, $value); if ($name == 'id') - return $this->assign_attribute($this->get_primary_key(true),$value); + return $this->assign_attribute($this->get_primary_key(true), $value); - foreach (static::$delegate as &$item) - { - if (($delegated_name = $this->is_delegated($name,$item))) + foreach (static::$delegate as &$item) { + if (($delegated_name = $this->is_delegated($name, $item))) return $this->{$item['to']}->{$delegated_name} = $value; } - throw new UndefinedPropertyException(get_called_class(),$name); + throw new UndefinedPropertyException(get_called_class(), $name); } public function __wakeup() @@ -455,7 +453,7 @@ public function assign_attribute($name, $value) $value = $table->columns[$name]->cast($value, static::connection()); } else { $col = $table->get_column_by_inflected_name($name); - if (!is_null($col)){ + if (!is_null($col)) { $value = $col->cast($value, static::connection()); } } @@ -475,7 +473,7 @@ public function assign_attribute($name, $value) if ($value instanceof DateTimeInterface) // Tell the Date object that it's associated with this model and attribute. This is so it // has the ability to flag this model as dirty if a field in the Date object changes. - $value->attribute_of($this,$name); + $value->attribute_of($this, $name); // ADDED BY FoxyCart $flag_as_dirty = (isset($this->attributes[$name]) && (string)$this->attributes[$name] === (string)$value) ? false : true; @@ -504,24 +502,22 @@ public function &read_attribute($name) $name = static::$alias_attribute[$name]; // check for attribute - if (array_key_exists($name,$this->attributes)) + if (array_key_exists($name, $this->attributes)) return $this->attributes[$name]; // check relationships if no attribute - if (array_key_exists($name,$this->__relationships)) + if (array_key_exists($name, $this->__relationships)) return $this->__relationships[$name]; $table = static::table(); // this may be first access to the relationship so check Table - if (($relationship = $table->get_relationship($name))) - { + if (($relationship = $table->get_relationship($name))) { $this->__relationships[$name] = $relationship->load($this); return $this->__relationships[$name]; } - if ($name == 'id') - { + if ($name == 'id') { $pk = $this->get_primary_key(true); if (isset($this->attributes[$pk])) return $this->attributes[$pk]; @@ -530,22 +526,18 @@ public function &read_attribute($name) //do not remove - have to return null by reference in strict mode $null = null; - foreach (static::$delegate as &$item) - { - if (($delegated_name = $this->is_delegated($name,$item))) - { + foreach (static::$delegate as &$item) { + if (($delegated_name = $this->is_delegated($name, $item))) { $to = $item['to']; - if ($this->$to) - { - $val =& $this->$to->__get($delegated_name); + if ($this->$to) { + $val = &$this->$to->__get($delegated_name); return $val; - } - else + } else return $null; } } - throw new UndefinedPropertyException(get_called_class(),$name); + throw new UndefinedPropertyException(get_called_class(), $name); } /** @@ -571,7 +563,7 @@ public function dirty_attributes() if (!$this->__dirty) return null; - $dirty = array_intersect_key($this->attributes,$this->__dirty); + $dirty = array_intersect_key($this->attributes, $this->__dirty); return !empty($dirty) ? $dirty : null; } @@ -601,7 +593,7 @@ public function attributes() * @param boolean Set to true to return the first value in the pk array only * @return string The primary key for the model */ - public function get_primary_key($first=false) + public function get_primary_key($first = false) { $pk = static::table()->pk; return $first ? $pk[0] : $pk; @@ -615,10 +607,10 @@ public function get_primary_key($first=false) */ public function get_real_attribute_name($name) { - if (array_key_exists($name,$this->attributes)) + if (array_key_exists($name, $this->attributes)) return $name; - if (array_key_exists($name,static::$alias_attribute)) + if (array_key_exists($name, static::$alias_attribute)) return static::$alias_attribute[$name]; return null; @@ -660,9 +652,8 @@ public function get_values_for($attributes) { $ret = array(); - foreach ($attributes as $name) - { - if (array_key_exists($name,$this->attributes)) + foreach ($attributes as $name) { + if (array_key_exists($name, $this->attributes)) $ret[$name] = $this->attributes[$name]; } return $ret; @@ -688,10 +679,13 @@ public static function table_name() */ private function is_delegated($name, &$delegate) { + if (!is_array($delegate)) + return null; + if ($delegate['prefix'] != '') - $name = substr($name,strlen($delegate['prefix'])+1); + $name = substr($name, strlen($delegate['prefix']) + 1); - if (is_array($delegate) && in_array($name,$delegate['delegate'])) + if (is_array($delegate) && in_array($name, $delegate['delegate'])) return $name; return null; @@ -734,7 +728,7 @@ private function verify_not_readonly($method_name) * * @param boolean $readonly Set to true to put the model into readonly mode */ - public function readonly($readonly=true) + public function readonly($readonly = true) { $this->__readonly = $readonly; } @@ -779,7 +773,7 @@ public static function table() * @param boolean $guard_attributes Set to true to guard protected/non-accessible attributes * @return Model */ - public static function create($attributes, $validate=true, $guard_attributes=true) + public static function create($attributes, $validate = true, $guard_attributes = true) { $class_name = get_called_class(); $model = new $class_name($attributes, $guard_attributes); @@ -799,7 +793,7 @@ public static function create($attributes, $validate=true, $guard_attributes=tru * @param boolean $validate Set to true or false depending on if you want the validators to run or not * @return boolean True if the model was saved to the database otherwise false */ - public function save($validate=true) + public function save($validate = true) { $this->verify_not_readonly('save'); return $this->is_new_record() ? $this->insert($validate) : $this->update($validate); @@ -812,11 +806,11 @@ public function save($validate=true) * @param boolean $validate Set to true or false depending on if you want the validators to run or not * @return boolean True if the model was saved to the database otherwise false */ - private function insert($validate=true) + private function insert($validate = true) { $this->verify_not_readonly('insert'); - if (($validate && !$this->_validate() || !$this->invoke_callback('before_create',false))) + if (($validate && !$this->_validate() || !$this->invoke_callback('before_create', false))) return false; $table = static::table(); @@ -827,26 +821,21 @@ private function insert($validate=true) $pk = $this->get_primary_key(true); $use_sequence = false; - if ($table->sequence && !isset($attributes[$pk])) - { - if (($conn = static::connection()) instanceof OciAdapter) - { + if ($table->sequence && !isset($attributes[$pk])) { + if (($conn = static::connection()) instanceof OciAdapter) { // terrible oracle makes us select the nextval first $attributes[$pk] = $conn->get_next_sequence_value($table->sequence); $table->insert($attributes); $this->attributes[$pk] = $attributes[$pk]; - } - else - { + } else { // unset pk that was set to null - if (array_key_exists($pk,$attributes)) + if (array_key_exists($pk, $attributes)) unset($attributes[$pk]); - $table->insert($attributes,$pk,$table->sequence); + $table->insert($attributes, $pk, $table->sequence); $use_sequence = true; } - } - else + } else $table->insert($attributes); // if we've got an autoincrementing/sequenced pk set it @@ -860,7 +849,7 @@ private function insert($validate=true) } $this->__new_record = false; - $this->invoke_callback('after_create',false); + $this->invoke_callback('after_create', false); $this->expire_cache(); return true; } @@ -872,26 +861,25 @@ private function insert($validate=true) * @param boolean $validate Set to true or false depending on if you want the validators to run or not * @return boolean True if the model was saved to the database otherwise false */ - private function update($validate=true) + private function update($validate = true) { $this->verify_not_readonly('update'); if ($validate && !$this->_validate()) return false; - if ($this->is_dirty()) - { + if ($this->is_dirty()) { $pk = $this->values_for_pk(); if (empty($pk)) throw new ActiveRecordException("Cannot update, no primary key defined for: " . get_called_class()); - if (!$this->invoke_callback('before_update',false)) + if (!$this->invoke_callback('before_update', false)) return false; $dirty = $this->dirty_attributes(); - static::table()->update($dirty,$pk); - $this->invoke_callback('after_update',false); + static::table()->update($dirty, $pk); + $this->invoke_callback('after_update', false); $this->expire_cache(); } @@ -901,8 +889,7 @@ private function update($validate=true) protected function expire_cache() { $table = static::table(); - if($table->cache_individual_model) - { + if ($table->cache_individual_model) { Cache::delete($this->cache_key()); } } @@ -947,7 +934,7 @@ protected function cache_key() * @params array $options * return integer Number of rows affected */ - public static function delete_all($options=array()) + public static function delete_all($options = array()) { $table = static::table(); $conn = static::connection(); @@ -1000,7 +987,7 @@ public static function delete_all($options=array()) * @params array $options * return integer Number of rows affected */ - public static function update_all($options=array()) + public static function update_all($options = array()) { $table = static::table(); $conn = static::connection(); @@ -1008,8 +995,7 @@ public static function update_all($options=array()) $sql->update($options['set']); - if (isset($options['conditions']) && ($conditions = $options['conditions'])) - { + if (isset($options['conditions']) && ($conditions = $options['conditions'])) { if (is_array($conditions) && !is_hash($conditions)) call_user_func_array(array($sql, 'where'), $conditions); else @@ -1025,7 +1011,6 @@ public static function update_all($options=array()) $values = $sql->bind_values(); $ret = $conn->query(($table->last_sql = $sql->to_s()), $values); return $ret->rowCount(); - } /** @@ -1042,11 +1027,11 @@ public function delete() if (empty($pk)) throw new ActiveRecordException("Cannot delete, no primary key defined for: " . get_called_class()); - if (!$this->invoke_callback('before_destroy',false)) + if (!$this->invoke_callback('before_destroy', false)) return false; static::table()->delete($pk); - $this->invoke_callback('after_destroy',false); + $this->invoke_callback('after_destroy', false); $this->expire_cache(); return true; @@ -1090,9 +1075,8 @@ private function _validate() $validator = new Validations($this); $validation_on = 'validation_on_' . ($this->is_new_record() ? 'create' : 'update'); - foreach (array('before_validation', "before_$validation_on") as $callback) - { - if (!$this->invoke_callback($callback,false)) + foreach (array('before_validation', "before_$validation_on") as $callback) { + if (!$this->invoke_callback($callback, false)) return false; } @@ -1101,7 +1085,7 @@ private function _validate() $validator->validate(); foreach (array('after_validation', "after_$validation_on") as $callback) - $this->invoke_callback($callback,false); + $this->invoke_callback($callback, false); if (!$this->errors->is_empty()) return false; @@ -1210,21 +1194,18 @@ private function set_attributes_via_mass_assignment(array &$attributes, $guard_a $use_attr_protected = !empty(static::$attr_protected); $connection = static::connection(); - foreach ($attributes as $name => $value) - { + foreach ($attributes as $name => $value) { // is a normal field on the table - if (array_key_exists($name,$table->columns)) - { - $value = $table->columns[$name]->cast($value,$connection); + if (array_key_exists($name, $table->columns)) { + $value = $table->columns[$name]->cast($value, $connection); $name = $table->columns[$name]->inflected_name; } - if ($guard_attributes) - { - if ($use_attr_accessible && !in_array($name,static::$attr_accessible)) + if ($guard_attributes) { + if ($use_attr_accessible && !in_array($name, static::$attr_accessible)) continue; - if ($use_attr_protected && in_array($name,static::$attr_protected)) + if ($use_attr_protected && in_array($name, static::$attr_protected)) continue; // set valid table data @@ -1233,20 +1214,18 @@ private function set_attributes_via_mass_assignment(array &$attributes, $guard_a } catch (UndefinedPropertyException $e) { $exceptions[] = $e->getMessage(); } - } - else - { + } else { // ignore OciAdapter's limit() stuff if ($name == 'ar_rnum__') continue; // set arbitrary data - $this->assign_attribute($name,$value); + $this->assign_attribute($name, $value); } } if (!empty($exceptions)) - throw new UndefinedPropertyException(get_called_class(),$exceptions); + throw new UndefinedPropertyException(get_called_class(), $exceptions); } /** @@ -1257,21 +1236,18 @@ private function set_attributes_via_mass_assignment(array &$attributes, $guard_a * @param $name of relationship for this table * @return void */ - public function set_relationship_from_eager_load(Model $model=null, $name) + public function set_relationship_from_eager_load(Model $model = null, $name) { $table = static::table(); - if (($rel = $table->get_relationship($name))) - { - if ($rel->is_poly()) - { + if (($rel = $table->get_relationship($name))) { + if ($rel->is_poly()) { // if the related model is null and it is a poly then we should have an empty array if (is_null($model)) return $this->__relationships[$name] = array(); else return $this->__relationships[$name][] = $model; - } - else + } else return $this->__relationships[$name] = $model; } @@ -1363,36 +1339,30 @@ public static function __callStatic($method, $args) $options = static::extract_and_validate_options($args); $create = false; - if (substr($method,0,17) == 'find_or_create_by') - { - $attributes = substr($method,17); + if (substr($method, 0, 17) == 'find_or_create_by') { + $attributes = substr($method, 17); // can't take any finders with OR in it when doing a find_or_create_by - if (strpos($attributes,'_or_') !== false) + if (strpos($attributes, '_or_') !== false) throw new ActiveRecordException("Cannot use OR'd attributes in find_or_create_by"); $create = true; - $method = 'find_by' . substr($method,17); + $method = 'find_by' . substr($method, 17); } - if (substr($method,0,7) === 'find_by') - { - $attributes = substr($method,8); - $options['conditions'] = SQLBuilder::create_conditions_from_underscored_string(static::connection(),$attributes,$args,static::$alias_attribute); + if (substr($method, 0, 7) === 'find_by') { + $attributes = substr($method, 8); + $options['conditions'] = SQLBuilder::create_conditions_from_underscored_string(static::connection(), $attributes, $args, static::$alias_attribute); - if (!($ret = static::find('first',$options)) && $create) - return static::create(SQLBuilder::create_hash_from_underscored_string($attributes,$args,static::$alias_attribute)); + if (!($ret = static::find('first', $options)) && $create) + return static::create(SQLBuilder::create_hash_from_underscored_string($attributes, $args, static::$alias_attribute)); return $ret; - } - elseif (substr($method,0,11) === 'find_all_by') - { - $options['conditions'] = SQLBuilder::create_conditions_from_underscored_string(static::connection(),substr($method,12),$args,static::$alias_attribute); - return static::find('all',$options); - } - elseif (substr($method,0,8) === 'count_by') - { - $options['conditions'] = SQLBuilder::create_conditions_from_underscored_string(static::connection(),substr($method,9),$args,static::$alias_attribute); + } elseif (substr($method, 0, 11) === 'find_all_by') { + $options['conditions'] = SQLBuilder::create_conditions_from_underscored_string(static::connection(), substr($method, 12), $args, static::$alias_attribute); + return static::find('all', $options); + } elseif (substr($method, 0, 8) === 'count_by') { + $options['conditions'] = SQLBuilder::create_conditions_from_underscored_string(static::connection(), substr($method, 9), $args, static::$alias_attribute); return static::count($options); } @@ -1409,8 +1379,7 @@ public static function __callStatic($method, $args) public function __call($method, $args) { //check for build|create_association methods - if (preg_match('/(build|create)_/', $method)) - { + if (preg_match('/(build|create)_/', $method)) { if (!empty($args)) $args = $args[0]; @@ -1419,8 +1388,8 @@ public function __call($method, $args) $table = static::table(); if (($association = $table->get_relationship($association_name)) || - ($association = $table->get_relationship(($association_name = Utils::pluralize($association_name))))) - { + ($association = $table->get_relationship(($association_name = Utils::pluralize($association_name)))) + ) { // access association to ensure that the relationship has been loaded // so that we do not double-up on records if we append a newly created $this->$association_name; @@ -1439,7 +1408,7 @@ public function __call($method, $args) */ public static function all(/* ... */) { - return call_user_func_array('static::find',array_merge(array('all'),func_get_args())); + return static::find('all', ...func_get_args()); } /** @@ -1458,18 +1427,17 @@ public static function count(/* ... */) $options = static::extract_and_validate_options($args); $options['select'] = 'COUNT(*)'; - if (!empty($args) && !is_null($args[0]) && !empty($args[0])) - { + if (!empty($args) && !is_null($args[0]) && !empty($args[0])) { if (is_hash($args[0])) $options['conditions'] = $args[0]; else - $options['conditions'] = call_user_func_array('static::pk_conditions',$args); + $options['conditions'] = static::pk_conditions(...$args); } $table = static::table(); $sql = $table->options_to_sql($options); $values = $sql->get_where_values(); - return static::connection()->query_and_fetch_one($sql->to_s(),$values); + return static::connection()->query_and_fetch_one($sql->to_s(), $values); } /** @@ -1486,7 +1454,7 @@ public static function count(/* ... */) */ public static function exists(/* ... */) { - return call_user_func_array('static::count',func_get_args()) > 0 ? true : false; + return call_user_func_array([static::class, 'count'], func_get_args()) > 0 ? true : false; } /** @@ -1497,7 +1465,7 @@ public static function exists(/* ... */) */ public static function first(/* ... */) { - return call_user_func_array('static::find',array_merge(array('first'),func_get_args())); + return static::find('first', ...func_get_args()); } /** @@ -1508,7 +1476,7 @@ public static function first(/* ... */) */ public static function last(/* ... */) { - return call_user_func_array('static::find',array_merge(array('last'),func_get_args())); + return static::find('last', ...func_get_args()); } /** @@ -1577,29 +1545,27 @@ public static function find(/* $type, $options */) $num_args = count($args); $single = true; - if ($num_args > 0 && ($args[0] === 'all' || $args[0] === 'first' || $args[0] === 'last')) - { - switch ($args[0]) - { + if ($num_args > 0 && ($args[0] === 'all' || $args[0] === 'first' || $args[0] === 'last')) { + switch ($args[0]) { case 'all': $single = false; break; - case 'last': - if (!array_key_exists('order',$options)) - $options['order'] = join(' DESC, ',static::table()->pk) . ' DESC'; + case 'last': + if (!array_key_exists('order', $options)) + $options['order'] = join(' DESC, ', static::table()->pk) . ' DESC'; else $options['order'] = SQLBuilder::reverse_order($options['order']); // fall thru - case 'first': + case 'first': $options['limit'] = 1; $options['offset'] = 0; - break; + break; } - $args = array_slice($args,1); + $args = array_slice($args, 1); $num_args--; } //find by pk @@ -1627,16 +1593,13 @@ protected static function get_models_from_cache($pks, $options) $models = array(); $table = static::table(); - if(!is_array($pks)) - { + if (!is_array($pks)) { $pks = array($pks); } - foreach($pks as $pk) - { + foreach ($pks as $pk) { $options['conditions'] = static::pk_conditions($pk); - $models[] = Cache::get($table->cache_key_for_model($pk), function() use ($table, $options) - { + $models[] = Cache::get($table->cache_key_for_model($pk), function () use ($table, $options) { $res = $table->find($options); return $res ? $res[0] : null; }, $table->cache_model_expire); @@ -1655,32 +1618,26 @@ protected static function get_models_from_cache($pks, $options) */ public static function find_by_pk($values, $options) { - if($values===null) - { - throw new RecordNotFound("Couldn't find ".get_called_class()." without an ID"); + if ($values === null) { + throw new RecordNotFound("Couldn't find " . get_called_class() . " without an ID"); } $table = static::table(); - if($table->cache_individual_model) - { + if ($table->cache_individual_model) { $list = static::get_models_from_cache($values, $options); - } - else - { + } else { $options['conditions'] = static::pk_conditions($values); $list = $table->find($options); } - $results = @count($list); + $results = is_countable($list) ? count($list) : 0; - if ($results != ($expected = @count($values))) - { + if ($results != ($expected = is_countable($values) ? count($values) : 1)) { $class = get_called_class(); if (is_array($values)) - $values = join(',',$values); + $values = join(',', $values); - if ($expected == 1) - { + if ($expected == 1) { throw new RecordNotFound("Couldn't find $class with ID=$values"); } @@ -1701,7 +1658,7 @@ public static function find_by_pk($values, $options) * @param array $values An array of values for any parameters that needs to be bound * @return array An array of models */ - public static function find_by_sql($sql, $values=null) + public static function find_by_sql($sql, $values = null) { return static::table()->find_by_sql($sql, $values, true); } @@ -1713,7 +1670,7 @@ public static function find_by_sql($sql, $values=null) * @param array $values Bind values, if any, for the query * @return object A PDOStatement object */ - public static function query($sql, $values=null) + public static function query($sql, $values = null) { return static::connection()->query($sql, $values); } @@ -1726,17 +1683,16 @@ public static function query($sql, $values=null) * @return boolean True if valid otherwise valse * @throws {@link ActiveRecordException} if the array contained any invalid options */ - public static function is_options_hash($array, $throw=true) + public static function is_options_hash($array, $throw = true) { - if (is_hash($array)) - { + if (is_hash($array)) { $keys = array_keys($array); - $diff = array_diff($keys,self::$VALID_OPTIONS); + $diff = array_diff($keys, self::$VALID_OPTIONS); if (!empty($diff) && $throw) - throw new ActiveRecordException("Unknown key(s): " . join(', ',$diff)); + throw new ActiveRecordException("Unknown key(s): " . join(', ', $diff)); - $intersect = array_intersect($keys,self::$VALID_OPTIONS); + $intersect = array_intersect($keys, self::$VALID_OPTIONS); if (!empty($intersect)) return true; @@ -1769,20 +1725,15 @@ public static function extract_and_validate_options(array &$array) { $options = array(); - if ($array) - { - $last = &$array[count($array)-1]; + if ($array) { + $last = &$array[count($array) - 1]; - try - { - if (self::is_options_hash($last)) - { + try { + if (self::is_options_hash($last)) { array_pop($array); $options = $last; } - } - catch (ActiveRecordException $e) - { + } catch (ActiveRecordException $e) { if (!is_hash($last)) throw $e; @@ -1799,7 +1750,7 @@ public static function extract_and_validate_options(array &$array) * @param array $options An array containing options for json serialization (see {@link Serialization} for valid options) * @return string JSON representation of the model */ - public function to_json(array $options=array()) + public function to_json(array $options = array()) { return $this->serialize('Json', $options); } @@ -1811,35 +1762,35 @@ public function to_json(array $options=array()) * @param array $options An array containing options for xml serialization (see {@link Serialization} for valid options) * @return string XML representation of the model */ - public function to_xml(array $options=array()) + public function to_xml(array $options = array()) { return $this->serialize('Xml', $options); } - /** - * Returns an CSV representation of this model. - * Can take optional delimiter and enclosure - * (defaults are , and double quotes) - * - * Ex: - * - * ActiveRecord\CsvSerializer::$delimiter=';'; - * ActiveRecord\CsvSerializer::$enclosure=''; - * YourModel::find('first')->to_csv(array('only'=>array('name','level'))); - * returns: Joe,2 - * - * YourModel::find('first')->to_csv(array('only_header'=>true,'only'=>array('name','level'))); - * returns: name,level - * - * - * @see Serialization - * @param array $options An array containing options for csv serialization (see {@link Serialization} for valid options) - * @return string CSV representation of the model - */ - public function to_csv(array $options=array()) - { - return $this->serialize('Csv', $options); - } + /** + * Returns an CSV representation of this model. + * Can take optional delimiter and enclosure + * (defaults are , and double quotes) + * + * Ex: + * + * ActiveRecord\CsvSerializer::$delimiter=';'; + * ActiveRecord\CsvSerializer::$enclosure=''; + * YourModel::find('first')->to_csv(array('only'=>array('name','level'))); + * returns: Joe,2 + * + * YourModel::find('first')->to_csv(array('only_header'=>true,'only'=>array('name','level'))); + * returns: name,level + * + * + * @see Serialization + * @param array $options An array containing options for csv serialization (see {@link Serialization} for valid options) + * @return string CSV representation of the model + */ + public function to_csv(array $options = array()) + { + return $this->serialize('Csv', $options); + } /** * Returns an Array representation of this model. @@ -1848,7 +1799,7 @@ public function to_csv(array $options=array()) * @param array $options An array containing options for json serialization (see {@link Serialization} for valid options) * @return array Array representation of the model */ - public function to_array(array $options=array()) + public function to_array(array $options = array()) { return $this->serialize('Array', $options); } @@ -1885,9 +1836,9 @@ private function serialize($type, $options) * @param boolean $must_exist Set to true to raise an exception if the callback does not exist. * @return boolean True if invoked or null if not */ - public function invoke_callback($method_name, $must_exist=true) + public function invoke_callback($method_name, $must_exist = true) { - return static::table()->callback->invoke($this,$method_name,$must_exist); + return static::table()->callback->invoke($this, $method_name, $must_exist); } /** @@ -1925,20 +1876,15 @@ public static function transaction($closure) { $connection = static::connection(); - try - { + try { $connection->transaction(); - if ($closure() === false) - { + if ($closure() === false) { $connection->rollback(); return false; - } - else + } else $connection->commit(); - } - catch (\Exception $e) - { + } catch (\Exception $e) { $connection->rollback(); throw $e; } diff --git a/lib/Relationship.php b/lib/Relationship.php index 9846b44..cb50af2 100644 --- a/lib/Relationship.php +++ b/lib/Relationship.php @@ -1,7 +1,9 @@ attribute_name = $options[0]; $this->options = $this->merge_association_options($options); @@ -126,7 +128,7 @@ public function is_poly() * @param $model_values_keys -> key(s)/value(s) to be used in query from model which is including * @return void */ - protected function query_and_attach_related_models_eagerly(Table $table, $models, $attributes, $includes=array(), $query_keys=array(), $model_values_keys=array()) + protected function query_and_attach_related_models_eagerly(Table $table, $models, $attributes, $includes = array(), $query_keys = array(), $model_values_keys = array()) { $values = array(); $options = $this->options; @@ -138,7 +140,7 @@ protected function query_and_attach_related_models_eagerly(Table $table, $models $values[] = $value[$inflector->variablize($model_values_key)]; $values = array($values); - $conditions = SQLBuilder::create_conditions_from_underscored_string($table->conn,$query_key,$values); + $conditions = SQLBuilder::create_conditions_from_underscored_string($table->conn, $query_key, $values); if (isset($options['conditions']) && strlen($options['conditions'][0]) > 1) Utils::add_condition($options['conditions'], $conditions); @@ -158,7 +160,7 @@ protected function query_and_attach_related_models_eagerly(Table $table, $models if (!isset($options['class_name'])) { $class = classify($options['through'], true); if (isset($this->options['namespace']) && !class_exists($class)) - $class = $this->options['namespace'].'\\'.$class; + $class = $this->options['namespace'] . '\\' . $class; $through_table = $class::table(); } else { @@ -185,18 +187,15 @@ protected function query_and_attach_related_models_eagerly(Table $table, $models $model_values_key = $inflector->variablize($model_values_key); $query_key = $inflector->variablize($query_key); - foreach ($related_models as $related) - { + foreach ($related_models as $related) { $related_models_map[$related->$query_key][] = $related; } - foreach ($models as $model) - { + foreach ($models as $model) { $key_to_match = $model->$model_values_key; if (isset($related_models_map[$key_to_match])) { - foreach ($related_models_map[$key_to_match] as $related) - { + foreach ($related_models_map[$key_to_match] as $related) { $hash = spl_object_hash($related); if (isset($used_models_map[$hash])) @@ -219,7 +218,7 @@ protected function query_and_attach_related_models_eagerly(Table $table, $models * @param array $attributes Hash containing attributes to initialize the model with * @return Model */ - public function build_association(Model $model, $attributes=array(), $guard_attributes=true) + public function build_association(Model $model, $attributes = array(), $guard_attributes = true) { $class_name = $this->class_name; return new $class_name($attributes, $guard_attributes); @@ -232,7 +231,7 @@ public function build_association(Model $model, $attributes=array(), $guard_attr * @param array $attributes Hash containing attributes to initialize the model with * @return Model */ - public function create_association(Model $model, $attributes=array(), $guard_attributes=true) + public function create_association(Model $model, $attributes = array(), $guard_attributes = true) { $class_name = $this->class_name; $new_record = $class_name::create($attributes, true, $guard_attributes); @@ -241,7 +240,7 @@ public function create_association(Model $model, $attributes=array(), $guard_att protected function append_record_to_associate(Model $associate, Model $record) { - $association =& $associate->{$this->attribute_name}; + $association = &$associate->{$this->attribute_name}; if ($this->poly_relationship) $association[] = $record; @@ -253,8 +252,8 @@ protected function append_record_to_associate(Model $associate, Model $record) protected function merge_association_options($options) { - $available_options = array_merge(self::$valid_association_options,static::$valid_association_options); - $valid_options = array_intersect_key(array_flip($available_options),$options); + $available_options = array_merge(self::$valid_association_options, static::$valid_association_options); + $valid_options = array_intersect_key(array_flip($available_options), $options); foreach ($valid_options as $option => $v) $valid_options[$option] = $options[$option]; @@ -264,8 +263,7 @@ protected function merge_association_options($options) protected function unset_non_finder_options($options) { - foreach (array_keys($options) as $option) - { + foreach (array_keys($options) as $option) { if (!in_array($option, Model::$VALID_OPTIONS)) unset($options[$option]); } @@ -282,16 +280,16 @@ protected function unset_non_finder_options($options) */ protected function set_inferred_class_name() { - $singularize = ($this instanceOf HasMany ? true : false); + $singularize = ($this instanceof HasMany ? true : false); $this->set_class_name(classify($this->attribute_name, $singularize)); } protected function set_class_name($class_name) { if (!has_absolute_namespace($class_name) && isset($this->options['namespace'])) { - $class_name = $this->options['namespace'].'\\'.$class_name; + $class_name = $this->options['namespace'] . '\\' . $class_name; } - + $reflection = Reflections::instance()->add($class_name)->get($class_name); if (!$reflection->isSubClassOf('ActiveRecord\\Model')) @@ -300,16 +298,16 @@ protected function set_class_name($class_name) $this->class_name = $class_name; } - protected function create_conditions_from_keys(Model $model, $condition_keys=array(), $value_keys=array()) + protected function create_conditions_from_keys(Model $model, $condition_keys = array(), $value_keys = array()) { $condition_string = implode('_and_', $condition_keys); $condition_values = array_values($model->get_values_for($value_keys)); // return null if all the foreign key values are null so that we don't try to do a query like "id is null" - if (all(null,$condition_values)) + if (all(null, $condition_values)) return null; - $conditions = SQLBuilder::create_conditions_from_underscored_string(Table::load(get_class($model))->conn,$condition_string,$condition_values); + $conditions = SQLBuilder::create_conditions_from_underscored_string(Table::load(get_class($model))->conn, $condition_string, $condition_values); # DO NOT CHANGE THE NEXT TWO LINES. add_condition operates on a reference and will screw options array up if (isset($this->options['conditions'])) @@ -328,49 +326,38 @@ protected function create_conditions_from_keys(Model $model, $condition_keys=arr * @param string $alias a table alias for when a table is being joined twice * @return string SQL INNER JOIN fragment */ - public function construct_inner_join_sql(Table $from_table, $using_through=false, $alias=null) + public function construct_inner_join_sql(Table $from_table, $using_through = false, $alias = null) { - if ($using_through) - { + if ($using_through) { $join_table = $from_table; $join_table_name = $from_table->get_fully_qualified_table_name(); $from_table_name = Table::load($this->class_name)->get_fully_qualified_table_name(); - } - else - { + } else { $join_table = Table::load($this->class_name); $join_table_name = $join_table->get_fully_qualified_table_name(); $from_table_name = $from_table->get_fully_qualified_table_name(); } // need to flip the logic when the key is on the other table - if ($this instanceof HasMany || $this instanceof HasOne) - { + if ($this instanceof HasMany || $this instanceof HasOne) { $this->set_keys($from_table->class->getName()); - if ($using_through) - { + if ($using_through) { $foreign_key = $this->primary_key[0]; $join_primary_key = $this->foreign_key[0]; - } - else - { + } else { $join_primary_key = $this->foreign_key[0]; $foreign_key = $this->primary_key[0]; } - } - else - { + } else { $foreign_key = $this->foreign_key[0]; $join_primary_key = $this->primary_key[0]; } - if (!is_null($alias)) - { + if (!is_null($alias)) { $aliased_join_table_name = $alias = $this->get_table()->conn->quote_name($alias); $alias .= ' '; - } - else + } else $aliased_join_table_name = $join_table_name; return "INNER JOIN $join_table_name {$alias}ON($from_table_name.$foreign_key = $aliased_join_table_name.$join_primary_key)"; @@ -454,12 +441,11 @@ class HasMany extends AbstractRelationship * @param array $options Options for the association * @return HasMany */ - public function __construct($options=array()) + public function __construct($options = array()) { parent::__construct($options); - if (isset($this->options['through'])) - { + if (isset($this->options['through'])) { $this->through = $this->options['through']; if (isset($this->options['source'])) @@ -473,7 +459,7 @@ public function __construct($options=array()) $this->set_inferred_class_name(); } - protected function set_keys($model_class_name, $override=false) + protected function set_keys($model_class_name, $override = false) { //infer from class_name if (!$this->foreign_key || $override) @@ -491,10 +477,8 @@ public function load(Model $model) // since through relationships depend on other relationships we can't do // this initiailization in the constructor since the other relationship // may not have been created yet and we only want this to run once - if (!isset($this->initialized)) - { - if ($this->through) - { + if (!isset($this->initialized)) { + if ($this->through) { // verify through is a belongs_to or has_many for access of keys if (!($through_relationship = $this->get_table()->get_relationship($this->through))) throw new HasManyThroughAssociationException("Could not find the association $this->through in model " . get_class($model)); @@ -507,7 +491,7 @@ public function load(Model $model) $fk = $this->foreign_key; $this->set_keys($this->get_table()->class->getName(), true); - + $class = $this->class_name; $relation = $class::table()->get_relationship($this->through); $through_table = $relation->get_table(); @@ -526,7 +510,7 @@ public function load(Model $model) $options = $this->unset_non_finder_options($this->options); $options['conditions'] = $conditions; - return $class_name::find($this->poly_relationship ? 'all' : 'first',$options); + return $class_name::find($this->poly_relationship ? 'all' : 'first', $options); } /** @@ -556,7 +540,7 @@ private function inject_foreign_key_for_new_association(Model $model, &$attribut return $attributes; } - public function build_association(Model $model, $attributes=array(), $guard_attributes=true) + public function build_association(Model $model, $attributes = array(), $guard_attributes = true) { $relationship_attributes = $this->get_foreign_key_for_new_association($model); @@ -577,7 +561,7 @@ public function build_association(Model $model, $attributes=array(), $guard_attr return $record; } - public function create_association(Model $model, $attributes=array(), $guard_attributes=true) + public function create_association(Model $model, $attributes = array(), $guard_attributes = true) { $relationship_attributes = $this->get_foreign_key_for_new_association($model); @@ -601,10 +585,10 @@ public function create_association(Model $model, $attributes=array(), $guard_att return $record; } - public function load_eagerly($models=array(), $attributes=array(), $includes, Table $table) + public function load_eagerly($models, $attributes, $includes, Table $table) { $this->set_keys($table->class->name); - $this->query_and_attach_related_models_eagerly($table,$models,$attributes,$includes,$this->foreign_key, $table->pk); + $this->query_and_attach_related_models_eagerly($table, $models, $attributes, $includes, $this->foreign_key, $table->pk); } } @@ -626,9 +610,7 @@ public function load_eagerly($models=array(), $attributes=array(), $includes, Ta * @package ActiveRecord * @see http://www.phpactiverecord.org/guides/associations */ -class HasOne extends HasMany -{ -} +class HasOne extends HasMany {} /** * @todo implement me @@ -637,7 +619,7 @@ class HasOne extends HasMany */ class HasAndBelongsToMany extends AbstractRelationship { - public function __construct($options=array()) + public function __construct($options = array()) { /* options => * join_table - name of the join table if not in lexical order @@ -648,10 +630,7 @@ public function __construct($options=array()) */ } - public function load(Model $model) - { - - } + public function load(Model $model) {} } /** @@ -685,7 +664,7 @@ public function load(Model $model) */ class BelongsTo extends AbstractRelationship { - public function __construct($options=array()) + public function __construct($options = array()) { parent::__construct($options); @@ -699,7 +678,7 @@ public function __construct($options=array()) public function __get($name) { - if($name === 'primary_key' && !isset($this->primary_key)) { + if ($name === 'primary_key' && !isset($this->primary_key)) { $this->primary_key = array(Table::load($this->class_name)->pk[0]); } @@ -723,8 +702,8 @@ public function load(Model $model) return $class::first($options); } - public function load_eagerly($models=array(), $attributes, $includes, Table $table) + public function load_eagerly($models, $attributes, $includes, Table $table) { - $this->query_and_attach_related_models_eagerly($table,$models,$attributes,$includes, $this->primary_key,$this->foreign_key); + $this->query_and_attach_related_models_eagerly($table, $models, $attributes, $includes, $this->primary_key, $this->foreign_key); } -} \ No newline at end of file +} diff --git a/lib/SQLBuilder.php b/lib/SQLBuilder.php index ef239ed..e3cffdf 100644 --- a/lib/SQLBuilder.php +++ b/lib/SQLBuilder.php @@ -388,7 +388,7 @@ private function build_select() private function build_update() { - if (strlen($this->update) > 0) + if (strlen($this->update ?? '') > 0) $set = $this->update; else $set = join('=?, ', $this->quoted_key_names()) . '=?'; @@ -419,4 +419,4 @@ private function quoted_key_names() return $keys; } -} \ No newline at end of file +} diff --git a/lib/Singleton.php b/lib/Singleton.php index fe370c2..dde0e52 100644 --- a/lib/Singleton.php +++ b/lib/Singleton.php @@ -1,7 +1,9 @@ heavily borrowed from Ruby on Rails' ActiveRecord so much that * this piece can be considered a straight port. The reason for this is that the vaildation process is @@ -9,6 +10,7 @@ */ namespace ActiveRecord; + use ActiveRecord\Model; use IteratorAggregate; use ArrayIterator; @@ -43,6 +45,7 @@ class Validations { private $model; private $options = array(); + private $klass; private $validators = array(); private $record; @@ -109,21 +112,19 @@ public function get_record() public function rules() { $data = array(); - foreach ($this->validators as $validate) - { + foreach ($this->validators as $validate) { $attrs = $this->klass->getStaticPropertyValue($validate); - foreach (wrap_strings_in_arrays($attrs) as $attr) - { + foreach (wrap_strings_in_arrays($attrs) as $attr) { $field = $attr[0]; if (!is_array($field)) { // ADDED BY FOXYCART - if (!isset($data[$field]) || !is_array($data[$field])) - $data[$field] = array(); + if (!isset($data[$field]) || !is_array($data[$field])) + $data[$field] = array(); - $attr['validator'] = $validate; - unset($attr[0]); - array_push($data[$field],$attr); + $attr['validator'] = $validate; + unset($attr[0]); + array_push($data[$field], $attr); } // ADDED BY FOXYCART } } @@ -137,8 +138,7 @@ public function rules() */ public function validate() { - foreach ($this->validators as $validate) - { + foreach ($this->validators as $validate) { $definition = $this->klass->getStaticPropertyValue($validate); $this->$validate(wrap_strings_in_arrays($definition)); } @@ -178,8 +178,7 @@ public function validates_presence_of($attrs) { $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES['blank'], 'on' => 'save')); - foreach ($attrs as $attr) - { + foreach ($attrs as $attr) { $options = array_merge($configuration, $attr); $this->record->add_on_blank($options[0], $options['message']); } @@ -253,8 +252,7 @@ public function validates_inclusion_or_exclusion_of($type, $attrs) { $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES[$type], 'on' => 'save')); - foreach ($attrs as $attr) - { + foreach ($attrs as $attr) { $options = array_merge($configuration, $attr); $attribute = $options[0]; $var = $this->model->$attribute; @@ -267,7 +265,7 @@ public function validates_inclusion_or_exclusion_of($type, $attrs) if (!is_array($enum)) array($enum); - $message = str_replace('%s', $var, $options['message']); + $message = str_replace('%s', $var ?? '', $options['message'] ?? ''); if ($this->is_null_with_option($var, $options) || $this->is_blank_with_option($var, $options)) continue; @@ -312,8 +310,7 @@ public function validates_numericality_of($attrs) // Notice that for fixnum and float columns empty strings are converted to nil. // Validates whether the value of the specified attribute is numeric by trying to convert it to a float with Kernel.Float // (if only_integer is false) or applying it to the regular expression /\A[+\-]?\d+\Z/ (if only_integer is set to true). - foreach ($attrs as $attr) - { + foreach ($attrs as $attr) { $options = array_merge($configuration, $attr); $attribute = $options[0]; $var = $this->model->$attribute; @@ -326,18 +323,13 @@ public function validates_numericality_of($attrs) $not_a_number_message = (isset($options['message']) ? $options['message'] : Errors::$DEFAULT_ERROR_MESSAGES['not_a_number']); - if (true === $options['only_integer'] && !is_integer($var)) - { - if (!preg_match('/\A[+-]?\d+\Z/', (string)($var))) - { + if (true === $options['only_integer'] && !is_integer($var)) { + if (!preg_match('/\A[+-]?\d+\Z/', (string)($var))) { $this->record->add($attribute, $not_a_number_message); continue; } - } - else - { - if (!is_numeric($var)) - { + } else { + if (!is_numeric($var)) { $this->record->add($attribute, $not_a_number_message); continue; } @@ -345,13 +337,11 @@ public function validates_numericality_of($attrs) $var = (float)$var; } - foreach ($numericalityOptions as $option => $check) - { + foreach ($numericalityOptions as $option => $check) { $option_value = $options[$option]; $message = (isset($options['message']) ? $options['message'] : Errors::$DEFAULT_ERROR_MESSAGES[$option]); - if ('odd' != $option && 'even' != $option) - { + if ('odd' != $option && 'even' != $option) { $option_value = (float)$options[$option]; if (!is_numeric($option_value)) @@ -373,9 +363,7 @@ public function validates_numericality_of($attrs) elseif ('less_than_or_equal_to' == $option && !($var <= $option_value)) $this->record->add($attribute, $message); - } - else - { + } else { if (('odd' == $option && !Utils::is_odd($var)) || ('even' == $option && Utils::is_odd($var))) $this->record->add($attribute, $message); } @@ -419,8 +407,7 @@ public function validates_format_of($attrs) { $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES['invalid'], 'on' => 'save', 'with' => null)); - foreach ($attrs as $attr) - { + foreach ($attrs as $attr) { $options = array_merge($configuration, $attr); $attribute = $options[0]; $var = $this->model->$attribute; @@ -470,14 +457,12 @@ public function validates_length_of($attrs) 'wrong_length' => Errors::$DEFAULT_ERROR_MESSAGES['wrong_length'] )); - foreach ($attrs as $attr) - { + foreach ($attrs as $attr) { $options = array_merge($configuration, $attr); $range_options = array_intersect(array_keys(self::$ALL_RANGE_OPTIONS), array_keys($attr)); sort($range_options); - switch (sizeof($range_options)) - { + switch (sizeof($range_options)) { case 0: throw new ValidationsArgumentError('Range unspecified. Specify the [within], [maximum], or [is] option.'); @@ -492,8 +477,7 @@ public function validates_length_of($attrs) $var = $this->model->$attribute; if ($this->is_null_with_option($var, $options) || $this->is_blank_with_option($var, $options)) continue; - if ($range_options[0] == 'within' || $range_options[0] == 'in') - { + if ($range_options[0] == 'within' || $range_options[0] == 'in') { $range = $options[$range_options[0]]; if (!(Utils::is_a('range', $range))) @@ -502,8 +486,7 @@ public function validates_length_of($attrs) $attr['minimum'] = $range[0]; $attr['maximum'] = $range[1]; } - foreach ($range_options as $range_option) - { + foreach ($range_options as $range_option) { $option = $attr[$range_option]; if ((int)$option <= 0) @@ -512,19 +495,18 @@ public function validates_length_of($attrs) if (is_float($option)) throw new ValidationsArgumentError("$range_option value cannot use a float for length."); - if (!($range_option == 'maximum' && is_null($this->model->$attribute))) - { + if (!($range_option == 'maximum' && is_null($this->model->$attribute))) { $messageOptions = array('is' => 'wrong_length', 'minimum' => 'too_short', 'maximum' => 'too_long'); if (isset($options['message'])) $message = $options['message']; else $message = $options[$messageOptions[$range_option]]; - + $message = str_replace('%d', $option, $message); $attribute_value = $this->model->$attribute; - $len = strlen($attribute_value); + $len = strlen($attribute_value ?? ''); $value = (int)$attr[$range_option]; if ('maximum' == $range_option && $len > $value) @@ -569,19 +551,15 @@ public function validates_uniqueness_of($attrs) 'message' => Errors::$DEFAULT_ERROR_MESSAGES['unique'] )); - foreach ($attrs as $attr) - { + foreach ($attrs as $attr) { $options = array_merge($configuration, $attr); $pk = $this->model->get_primary_key(); $pk_value = $this->model->{$pk[0]}; - if (is_array($options[0])) - { + if (is_array($options[0])) { $add_record = join("_and_", $options[0]); $fields = $options[0]; - } - else - { + } else { $add_record = $options[0]; $fields = array($options[0]); } @@ -592,19 +570,17 @@ public function validates_uniqueness_of($attrs) if ($pk_value === null) $sql = "{$pk[0]} is not null"; - else - { + else { $sql = "{$pk[0]}!=?"; - array_push($conditions,$pk_value); + array_push($conditions, $pk_value); } - foreach ($fields as $field) - { + foreach ($fields as $field) { $field = $this->model->get_real_attribute_name($field); $value = $this->model->$field; if (!is_null($value)) { $sql .= " and {$field}=?"; - array_push($conditions,$value); + array_push($conditions, $value); $unique = false; } } @@ -689,7 +665,7 @@ public function clear_model() public function add($attribute, $msg) { if (is_null($msg)) - $msg = self :: $DEFAULT_ERROR_MESSAGES['invalid']; + $msg = self::$DEFAULT_ERROR_MESSAGES['invalid']; if (!isset($this->errors[$attribute])) $this->errors[$attribute] = array($msg); @@ -800,7 +776,7 @@ public function full_messages() { $full_messages = array(); - $this->to_array(function($attribute, $message) use (&$full_messages) { + $this->to_array(function ($attribute, $message) use (&$full_messages) { $full_messages[] = $message; }); @@ -824,16 +800,13 @@ public function full_messages() * and is called for each available error message. * @return array */ - public function to_array($closure=null) + public function to_array($closure = null) { $errors = array(); - if ($this->errors) - { - foreach ($this->errors as $attribute => $messages) - { - foreach ($messages as $msg) - { + if ($this->errors) { + foreach ($this->errors as $attribute => $messages) { + foreach ($messages as $msg) { if (is_null($msg)) continue; @@ -842,7 +815,7 @@ public function to_array($closure=null) //$errors[$attribute][] = ($message = Utils::human_attribute($attribute) . ' ' . $msg); if ($closure) - $closure($attribute,$message); + $closure($attribute, $message); } } } From 156daa5088517b4ab82b7a0f3e6fb77f083cc752 Mon Sep 17 00:00:00 2001 From: filipcujanovic <29860740+filipcujanovic@users.noreply.github.com> Date: Thu, 7 May 2026 19:59:14 +0200 Subject: [PATCH 3/6] fix(DateTimeInterface): update method signatures --- lib/DateTimeInterface.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/DateTimeInterface.php b/lib/DateTimeInterface.php index 3e7ca31..ada18e6 100644 --- a/lib/DateTimeInterface.php +++ b/lib/DateTimeInterface.php @@ -1,9 +1,13 @@ assign_attribute() will * know to call attribute_of() on passed values. This is so the DateTime object can flag the model @@ -18,7 +22,7 @@ interface DateTimeInterface * Indicates this object is an attribute of the specified model, with the given attribute name. * * @param Model $model The model this object is an attribute of - * @param string $attribute_name The attribute name + * @param string $attribute_name The attribute name * @return void */ public function attribute_of($model, $attribute_name); @@ -26,10 +30,10 @@ public function attribute_of($model, $attribute_name); /** * Formats the DateTime to the specified format. */ - public function format($format=null); + public function format(?string $format = null): string; /** * See http://php.net/manual/en/datetime.createfromformat.php */ - public static function createFromFormat($format, $time, $tz = null); + public static function createFromFormat(string $format, string $time, ?DateTimeZone $tz = null): static|false; } From c8859b8c8aa43a95222c8ee79febdc2ce8002c12 Mon Sep 17 00:00:00 2001 From: filipcujanovic <29860740+filipcujanovic@users.noreply.github.com> Date: Thu, 7 May 2026 20:03:40 +0200 Subject: [PATCH 4/6] fix(Validations): update method signature --- lib/Validations.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Validations.php b/lib/Validations.php index 53b9571..bc11e52 100644 --- a/lib/Validations.php +++ b/lib/Validations.php @@ -884,7 +884,7 @@ public function size() * * @return ArrayIterator */ - public function getIterator() + public function getIterator(): ArrayIterator { return new ArrayIterator($this->full_messages()); } From 66ca7030f56ade6c06700940a70d7be0139b6c1e Mon Sep 17 00:00:00 2001 From: Vladimir Serov Date: Wed, 3 Jun 2026 22:12:39 +0400 Subject: [PATCH 5/6] fix: guard castIntegerSafely against non-numeric strings on PHP 8 --- lib/Column.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Column.php b/lib/Column.php index ba8517b..703ad52 100644 --- a/lib/Column.php +++ b/lib/Column.php @@ -130,7 +130,7 @@ public static function castIntegerSafely($value) // If adding 0 to a string causes a float conversion, // we have a number over PHP_INT_MAX - elseif (is_string($value) && is_float($value + 0)) + elseif (is_string($value) && is_numeric($value) && is_float($value + 0)) return (string) $value; // If a float was passed and its greater than PHP_INT_MAX From 39d50eb07c740f28e73f3d1fea931f62b02fd98c Mon Sep 17 00:00:00 2001 From: Vladimir Serov Date: Wed, 3 Jun 2026 22:19:38 +0400 Subject: [PATCH 6/6] fix: rename package to foxycart/php-activerecord, update autoload --- composer.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 63f9329..c1370a9 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "php-activerecord/php-activerecord", + "name": "foxycart/php-activerecord", "type": "library", "description": "php-activerecord is an open source ORM library based on the ActiveRecord pattern.", "keywords": ["activerecord", "orm"], @@ -14,6 +14,7 @@ "pear/log": "~1.12" }, "autoload": { - "files": [ "ActiveRecord.php" ] + "classmap": ["lib/"], + "files": ["lib/Utils.php"] } }