/**
  * @var string[] $row
  * @var PostRevision|null $obj
  * @return PostRevision
  * @throws DataModelException
  */
 public static function fromStorageRow(array $row, $obj = null)
 {
     /** @var $obj PostRevision */
     $obj = parent::fromStorageRow($row, $obj);
     $treeRevId = UUID::create($row['tree_rev_id']);
     if (!$obj->revId->equals($treeRevId)) {
         throw new DataModelException('tree revision doesn\'t match provided revision: ' . $treeRevId->getAlphadecimal() . ' != ' . $obj->revId->getAlphadecimal(), 'process-data');
     }
     $obj->replyToId = UUID::create($row['tree_parent_id']);
     $obj->postId = UUID::create($row['rev_type_id']);
     $obj->origUser = UserTuple::newFromArray($row, 'tree_orig_user_');
     if (!$obj->origUser) {
         throw new DataModelException('Could not create UserTuple for tree_orig_user_');
     }
     return $obj;
 }
 /**
  * @param string[] $row
  * @param AbstractRevision|null $obj
  * @return AbstractRevision
  * @throws DataModelException
  */
 public static function fromStorageRow(array $row, $obj = null)
 {
     if ($obj === null) {
         /** @var AbstractRevision $obj */
         $obj = new static();
     } elseif (!$obj instanceof static) {
         throw new DataModelException('wrong object type', 'process-data');
     }
     $obj->revId = UUID::create($row['rev_id']);
     $obj->user = UserTuple::newFromArray($row, 'rev_user_');
     if ($obj->user === null) {
         throw new DataModelException('Could not load UserTuple for rev_user_');
     }
     $obj->prevRevision = UUID::create($row['rev_parent_id']);
     $obj->changeType = $row['rev_change_type'];
     $obj->flags = array_filter(explode(',', $row['rev_flags']));
     $obj->content = $row['rev_content'];
     // null if external store is not being used
     $obj->contentUrl = isset($row['rev_content_url']) ? $row['rev_content_url'] : null;
     $obj->decompressedContent = null;
     $obj->moderationState = $row['rev_mod_state'];
     $obj->moderatedBy = UserTuple::newFromArray($row, 'rev_mod_user_');
     $obj->moderationTimestamp = $row['rev_mod_timestamp'];
     $obj->moderatedReason = isset($row['rev_mod_reason']) ? $row['rev_mod_reason'] : null;
     // BC: 'suppress' used to be called 'censor' & 'lock' was 'close'
     $bc = array('censor' => AbstractRevision::MODERATED_SUPPRESSED, 'close' => AbstractRevision::MODERATED_LOCKED);
     $obj->moderationState = str_replace(array_keys($bc), array_values($bc), $obj->moderationState);
     // isset required because there is a possible db migration, cached data will not have it
     $obj->lastEditId = isset($row['rev_last_edit_id']) ? UUID::create($row['rev_last_edit_id']) : null;
     $obj->lastEditUser = UserTuple::newFromArray($row, 'rev_edit_user_');
     $obj->contentLength = isset($row['rev_content_length']) ? $row['rev_content_length'] : 0;
     $obj->previousContentLength = isset($row['rev_previous_content_length']) ? $row['rev_previous_content_length'] : 0;
     return $obj;
 }