From 29978b7a1ef3cbed8e63a0479b8bd1e556a3edfe Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 8 Jul 2014 20:39:51 +0000 Subject: [PATCH] Resolve issue with recursing. Curse you, PHP! --- includes/tuque_wrapper.inc | 76 +++++++++++++++++++++++++++++-- tests/hooks.test | 4 ++ tests/islandora_hooks_test.module | 8 ++++ 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/includes/tuque_wrapper.inc b/includes/tuque_wrapper.inc index 33881193..348ac60a 100644 --- a/includes/tuque_wrapper.inc +++ b/includes/tuque_wrapper.inc @@ -144,6 +144,41 @@ class IslandoraFedoraObject extends FedoraObject { protected $fedoraDatastreamClass = 'IslandoraFedoraDatastream'; protected $fedoraRelsExtClass = 'IslandoraFedoraRelsExt'; + /** + * Magical magic, to allow recursive modifications. + * + * So... Magic functions in PHP are not re-entrant... Meaning that if you + * have something which tries to call __set on an object anywhere later in + * the callstack after it has already been called, it will not call the + * magic method again; instead, it will set the property on the object + * proper. Here, we detect the property being set on the object proper, and + * restore the magic functionality as long as it keeps getting set... + * + * Not necessary to try to account for this in Tuque proper, as Tuque itself + * does not have a mechanism to trigger modifications resulting from other + * modifications. + * + * @param string $name + * The name of the property being set. + * @param mixed $value + * The value to which the property should be set. + */ + public function __set($name, $value) { + parent::__set($name, $value); + + // XXX: Due to the structure of the code, we cannot use property_exists() + // (because many of the properties are declared in the class, and the magic + // triggers due them being NULLed), nor can we use isset() (because it is + // implemented as another magic function...). + $vars = get_object_vars($this); + while (isset($vars[$name])) { + $new_value = $this->$name; + unset($this->$name); + parent::__set($name, $new_value); + $vars = get_object_vars($this); + } + } + /** * Ingest the given datastream. * @@ -373,6 +408,41 @@ class IslandoraFedoraDatastream extends FedoraDatastream { protected $fedoraRelsIntClass = 'IslandoraFedoraRelsInt'; protected $fedoraDatastreamVersionClass = 'IslandoraFedoraDatastreamVersion'; + /** + * Magical magic, to allow recursive modifications. + * + * So... Magic functions in PHP are not re-entrant... Meaning that if you + * have something which tries to call __set on an object anywhere later in + * the callstack after it has already been called, it will not call the + * magic method again; instead, it will set the property on the object + * proper. Here, we detect the property being set on the object proper, and + * restore the magic functionality as long as it keeps getting set... + * + * Not necessary to try to account for this in Tuque proper, as Tuque itself + * does not have a mechanism to trigger modifications resulting from other + * modifications. + * + * @param string $name + * The name of the property being set. + * @param mixed $value + * The value to which the property should be set. + */ + public function __set($name, $value) { + parent::__set($name, $value); + + // XXX: Due to the structure of the code, we cannot use property_exists() + // (because many of the properties are declared in the class, and the magic + // triggers due them being NULLed), nor can we use isset() (because it is + // implemented as another magic function...). + $vars = get_object_vars($this); + while (isset($vars[$name])) { + $new_value = $this->$name; + unset($this->$name); + parent::__set($name, $new_value); + $vars = get_object_vars($this); + } + } + /** * Inherits. * @@ -383,10 +453,8 @@ class IslandoraFedoraDatastream extends FedoraDatastream { protected function modifyDatastream(array $args) { try { parent::modifyDatastream($args); - // XXX: Reload datastream from parent. - $datastream = $this->parent[$this->id]; - islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_MODIFIED_HOOK, $this->parent->models, $this->id, $this->parent, $datastream); - if ($datastream->state == 'D') { + islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_MODIFIED_HOOK, $this->parent->models, $this->id, $this->parent, $this); + if ($this->state == 'D') { islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_PURGED_HOOK, $this->parent->models, $this->id, $this->parent, $this->id); } } diff --git a/tests/hooks.test b/tests/hooks.test index d08c537b..b8846c52 100644 --- a/tests/hooks.test +++ b/tests/hooks.test @@ -109,9 +109,11 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->repository->ingestObject($object); $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK] = FALSE; $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_MODIFIED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['iteration'][ISLANDORA_OBJECT_MODIFIED_HOOK] = 0; $object->label = "New Label!"; $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called "hook_islandora_object_alter" when modifying via set magic functions.'); $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called ISLANDORA_OBJECT_MODIFIED_HOOK when modifying via set magic functions.'); + $this->assertEqual('New Label! + 3', $object->label, 'Re-entered ISLANDORA_OBJECT_MODIFIED_HOOK when modifying via set magic functions.'); // Test blocking the modification. try { @@ -191,10 +193,12 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $ds = $object->constructDatastream('TEST'); $object->ingestDatastream($ds); $_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['iteration'][ISLANDORA_DATASTREAM_MODIFIED_HOOK] = 0; $_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_MODIFIED_HOOK] = FALSE; $ds->label = "New Label!"; $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called "hook_islandora_datastream_alter" when modifying via set magic functions.'); $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called ISLANDORA_DATASTREAM_MODIFIED_HOOK when modifying via set magic functions.'); + $this->assertEqual('New Label! + 3', $ds->label, 'Re-entered ISLANDORA_DATASTREAM_MODIFIED_HOOK when modifying via set magic functions.'); // Test blocking modifying. try { diff --git a/tests/islandora_hooks_test.module b/tests/islandora_hooks_test.module index a6b8a5b6..cceb4dbc 100644 --- a/tests/islandora_hooks_test.module +++ b/tests/islandora_hooks_test.module @@ -112,6 +112,10 @@ function islandora_hooks_test_islandora_object_ingested(AbstractObject $object) function islandora_hooks_test_islandora_object_modified(AbstractObject $object) { if ($object->id == 'test:testModifiedObjectHook') { $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK] = TRUE; + if ($_SESSION['islandora_hooks']['iteration'][ISLANDORA_OBJECT_MODIFIED_HOOK]++ < 3) { + $new_label = 'New Label! + ' . $_SESSION['islandora_hooks']['iteration'][ISLANDORA_OBJECT_MODIFIED_HOOK]; + $object->label = $new_label; + } } } @@ -139,6 +143,10 @@ function islandora_hooks_test_islandora_datastream_ingested(AbstractObject $obje function islandora_hooks_test_islandora_datastream_modified(AbstractObject $object, AbstractDatastream $datastream) { if ($object->id == 'test:testModifiedDatastreamHook' && $datastream->id == "TEST") { $_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK] = TRUE; + if ($_SESSION['islandora_hooks']['iteration'][ISLANDORA_DATASTREAM_MODIFIED_HOOK]++ < 3) { + $new_label = 'New Label! + ' . $_SESSION['islandora_hooks']['iteration'][ISLANDORA_DATASTREAM_MODIFIED_HOOK]; + $datastream->label = $new_label; + } } }