diff --git a/src/db/orm/PersistableObject.php b/src/db/orm/PersistableObject.php index 897fb00a..4917f767 100644 --- a/src/db/orm/PersistableObject.php +++ b/src/db/orm/PersistableObject.php @@ -23,7 +23,13 @@ abstract class PersistableObject extends CObject { /** @var bool - dirty checking status */ - private $_modified; + private $__modified = false; + + /** @var bool - flag to detect and handle recursive $this->save() calls */ + private $__inSave = false; + + /** @var bool - flag to detect and handle recursive $this->delete() calls */ + private $__inDelete = false; /** @@ -347,7 +353,7 @@ final public function isDeleted() { * @return bool */ final public function isModified() { - return (bool) $this->_modified; + return (bool) $this->__modified; } @@ -357,7 +363,7 @@ final public function isModified() { * @return $this */ final protected function modified() { - $this->_modified = true; + $this->__modified = true; return $this; } @@ -368,24 +374,34 @@ final protected function modified() { * @return $this */ public function save() { - if (!$this->isPersistent()) { - $this->dao()->transaction(function() { - if ($this->beforeSave() !== true) // pre-processing hook - return $this; - $this->insert(); - $this->afterSave(); // post-processing hook - }); - } - elseif ($this->isModified()) { - $this->dao()->transaction(function() { - if ($this->beforeSave() !== true) // pre-processing hook - return $this; - $this->update(); - $this->afterSave(); // post-processing hook - }); + if ($this->__inSave) // skip recursive calls from pre/post-processing hooks + return $this; + + try { + $this->__inSave = true; + + if (!$this->isPersistent()) { + $this->dao()->transaction(function() { + if ($this->beforeSave() !== true) // pre-processing hook + return $this; + $this->insert(); + $this->afterSave(); // post-processing hook + }); + } + elseif ($this->isModified()) { + $this->dao()->transaction(function() { + if ($this->beforeSave() !== true) // pre-processing hook + return $this; + $this->update(); + $this->afterSave(); // post-processing hook + }); + } + else { + // persistent but not modified + } } - else { - // persistent but not modified + finally { + $this->__inSave = false; } return $this; } @@ -413,7 +429,7 @@ private function insert() { // perform insertion $id = $this->doInsert($values); - $this->_modified = false; + $this->__modified = false; // assign the returned identity value $idName = $mapping['identity']['name']; @@ -447,7 +463,7 @@ private function update() { // perform update if ($this->doUpdate($changes)) { - $this->_modified = false; + $this->__modified = false; // post-processing hook $this->afterUpdate(); @@ -462,20 +478,29 @@ private function update() { * @return $this */ public function delete() { - if (!$this->isPersistent()) throw new IllegalStateException('Cannot delete non-persistent '.get_class($this)); + if ($this->__inDelete) // skip recursive calls from pre/post-processing hooks + return $this; - $this->dao()->transaction(function() { - if ($this->beforeDelete() !== true) // pre-processing hook - return $this; + try { + $this->__inDelete = true; + if (!$this->isPersistent()) throw new IllegalStateException('Cannot delete non-persistent '.get_class($this)); - if ($this->doDelete()) { // perform deletion - // reset identity property - $idName = $this->dao()->getMapping()['identity']['name']; - $this->$idName = null; + $this->dao()->transaction(function() { + if ($this->beforeDelete() !== true) // pre-processing hook + return $this; - $this->afterDelete(); // post-processing hook - } - }); + if ($this->doDelete()) { // perform deletion + // reset identity property + $idName = $this->dao()->getMapping()['identity']['name']; + $this->$idName = null; + + $this->afterDelete(); // post-processing hook + } + }); + } + finally { + $this->__inDelete = false; + } return $this; }