diff --git a/src/applications/audit/editor/PhabricatorAuditEditor.php b/src/applications/audit/editor/PhabricatorAuditEditor.php --- a/src/applications/audit/editor/PhabricatorAuditEditor.php +++ b/src/applications/audit/editor/PhabricatorAuditEditor.php @@ -949,6 +949,12 @@ ); } + protected function getCustomWorkerStateEncoding() { + return array( + 'rawPatch' => self::STORAGE_ENCODING_BINARY, + ); + } + protected function loadCustomWorkerState(array $state) { $this->rawPatch = idx($state, 'rawPatch'); $this->affectedFiles = idx($state, 'affectedFiles'); diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -69,6 +69,8 @@ private $feedNotifyPHIDs = array(); private $feedRelatedPHIDs = array(); + const STORAGE_ENCODING_BINARY = 'binary'; + /** * Get the class name for the application this editor is a part of. * @@ -2637,6 +2639,21 @@ } + /** + * @task mail + */ + private function runHeraldMailRules(array $messages) { + foreach ($messages as $message) { + $engine = new HeraldEngine(); + $adapter = id(new PhabricatorMailOutboundMailHeraldAdapter()) + ->setObject($message); + + $rules = $engine->loadRulesForAdapter($adapter); + $effects = $engine->applyRules($rules, $adapter); + $engine->applyEffects($effects, $adapter, $rules); + } + } + /* -( Publishing Feed Stories )-------------------------------------------- */ @@ -3060,9 +3077,13 @@ $state[$property] = $this->$property; } + $custom_state = $this->getCustomWorkerState(); + $custom_encoding = $this->getCustomWorkerStateEncoding(); + $state += array( 'excludeMailRecipientPHIDs' => $this->getExcludeMailRecipientPHIDs(), - 'custom' => $this->getCustomWorkerState(), + 'custom' => $this->encodeStateForStorage($custom_state, $custom_encoding), + 'custom.encoding' => $custom_encoding, ); return $state; @@ -3081,6 +3102,21 @@ /** + * Hook; return storage encoding for custom properties which need to be + * passed to workers. + * + * This primarily allows binary data to be passed to workers and survive + * JSON encoding. + * + * @return dict Property encodings. + * @task workers + */ + protected function getCustomWorkerStateEncoding() { + return array(); + } + + + /** * Load editor state using a dictionary emitted by @{method:getWorkerState}. * * This method is used to load state when running worker operations. @@ -3097,7 +3133,10 @@ $exclude = idx($state, 'excludeMailRecipientPHIDs', array()); $this->setExcludeMailRecipientPHIDs($exclude); - $custom = idx($state, 'custom', array()); + $custom_state = idx($state, 'custom', array()); + $custom_encodings = idx($state, 'custom.encoding', array()); + $custom = $this->decodeStateFromStorage($custom_state, $custom_encodings); + $this->loadCustomWorkerState($custom); return $this; @@ -3143,16 +3182,85 @@ ); } - private function runHeraldMailRules(array $messages) { - foreach ($messages as $message) { - $engine = new HeraldEngine(); - $adapter = id(new PhabricatorMailOutboundMailHeraldAdapter()) - ->setObject($message); + /** + * Apply encodings prior to storage. + * + * See @{method:getCustomWorkerStateEncoding}. + * + * @param map Map of values to encode. + * @param map Map of encodings to apply. + * @return map Map of encoded values. + * @task workers + */ + final private function encodeStateForStorage( + array $state, + array $encodings) { + + foreach ($state as $key => $value) { + $encoding = idx($encodings, $key); + switch ($encoding) { + case self::STORAGE_ENCODING_BINARY: + // The mechanics of this encoding (serialize + base64) are a little + // awkward, but it allows us encode arrays and still be JSON-safe + // with binary data. + + $value = @serialize($value); + if ($value === false) { + throw new Exception( + pht( + 'Failed to serialize() value for key "%s".', + $key)); + } - $rules = $engine->loadRulesForAdapter($adapter); - $effects = $engine->applyRules($rules, $adapter); - $engine->applyEffects($effects, $adapter, $rules); + $value = base64_encode($value); + if ($value === false) { + throw new Exception( + pht( + 'Failed to base64 encode value for key "%s".', + $key)); + } + break; + } + $state[$key] = $value; } + + return $state; + } + + + /** + * Undo storage encoding applied when storing state. + * + * See @{method:getCustomWorkerStateEncoding}. + * + * @param map Map of encoded values. + * @param map Map of encodings. + * @return map Map of decoded values. + * @task workers + */ + final private function decodeStateFromStorage( + array $state, + array $encodings) { + + foreach ($state as $key => $value) { + $encoding = idx($encodings, $key); + switch ($encoding) { + case self::STORAGE_ENCODING_BINARY: + $value = base64_decode($value); + if ($value === false) { + throw new Exception( + pht( + 'Failed to base64_decode() value for key "%s".', + $key)); + } + + $value = unserialize($value); + break; + } + $state[$key] = $value; + } + + return $state; } } diff --git a/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php b/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php --- a/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php +++ b/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php @@ -26,9 +26,14 @@ * Load the object the transactions affect. */ private function loadObject() { - $data = $this->getTaskData(); $viewer = PhabricatorUser::getOmnipotentUser(); + $data = $this->getTaskData(); + if (!is_array($data)) { + throw new PhabricatorWorkerPermanentFailureException( + pht('Task has invalid task data.')); + } + $phid = idx($data, 'objectPHID'); if (!$phid) { throw new PhabricatorWorkerPermanentFailureException( diff --git a/src/infrastructure/storage/lisk/LiskDAO.php b/src/infrastructure/storage/lisk/LiskDAO.php --- a/src/infrastructure/storage/lisk/LiskDAO.php +++ b/src/infrastructure/storage/lisk/LiskDAO.php @@ -1651,7 +1651,7 @@ if ($deserialize) { $data[$col] = json_decode($data[$col], true); } else { - $data[$col] = json_encode($data[$col]); + $data[$col] = phutil_json_encode($data[$col]); } break; default: