function synchronize($task, $entitiesToSynchronize = null) { $this->maybeInit($entitiesToSynchronize); $options = $this->options; if (count($options) > 0) { $syncQuery = "INSERT INTO {$this->tableName} (option_name, option_value, autoload) VALUES "; foreach ($options as $optionName => $option) { $option = $this->urlReplacer->restore($option); $option = $this->maybeRestoreReference($option); if (!isset($option['autoload'])) { $option['autoload'] = 'yes'; } // default value if (!isset($option['option_value'])) { $option['option_value'] = ''; } $syncQuery .= "(\"{$optionName}\", \"" . $this->database->_real_escape($option['option_value']) . "\", \"{$option['autoload']}\"),"; } $syncQuery[strlen($syncQuery) - 1] = " "; // strip last comma $syncQuery .= " ON DUPLICATE KEY UPDATE option_value = VALUES(option_value), autoload = VALUES(autoload);"; $this->database->query($syncQuery); } $ignoredOptionNames = ArrayUtils::column($options, 'option_name'); $ignoredOptionNames = array_merge($ignoredOptionNames, OptionStorage::$optionsBlacklist); $deleteSql = "DELETE FROM {$this->tableName} WHERE option_name NOT IN (\"" . join('", "', $ignoredOptionNames) . "\") OR option_name NOT LIKE '_%'"; if ($entitiesToSynchronize) { $synchronizedOptions = ArrayUtils::column($entitiesToSynchronize, 'vp_id'); $deleteSql = "DELETE FROM {$this->tableName} WHERE option_name NOT IN (\"" . join('", "', $ignoredOptionNames) . "\") AND option_name IN (\"" . join('", "', $synchronizedOptions) . "\")"; } $this->database->query($deleteSql); return array(); }
public function restoreAllDefinitionFilesFromHistory() { FileSystem::removeContent($this->directory); $definitionFilesWildcard = WP_PLUGIN_DIR . '/*/.versionpress/actions.yml'; $modifications = $this->gitRepository->getFileModifications($definitionFilesWildcard); $modifications = array_filter($modifications, function ($modification) { return $modification['status'] !== 'D'; }); $lastModifications = ArrayUtils::unique($modifications, function ($modification) { return $modification['path']; }); foreach ($lastModifications as $modification) { $fileContent = $this->gitRepository->getFileInRevision($modification['path'], $modification['commit']); $plugin = basename(dirname(dirname($modification['path']))); $targetFile = $this->getDefinitionFileName($plugin); file_put_contents($targetFile, $fileContent); } $this->saveDefinitionForPlugin('versionpress/versionpress.php'); }
private function containsTermChangeInfo($changeInfoList) { return ArrayUtils::any($changeInfoList, function ($changeInfo) { return $changeInfo instanceof TermChangeInfo; }); }
private function getIdsForVpIds($referencesToUpdate) { if (count($referencesToUpdate) === 0) { return array(array(0, 0)); } $vpIdTable = $this->dbSchema->getPrefixedTableName('vp_id'); $vpIds = array_map(function ($vpId) { return 'UNHEX("' . $vpId . '")'; }, $referencesToUpdate); $vpIdsRestriction = join(', ', $vpIds); $result = $this->database->get_results("SELECT HEX(vp_id), id FROM {$vpIdTable} WHERE vp_id IN ({$vpIdsRestriction})", ARRAY_N); $result[] = array(0, 0); return array_combine(ArrayUtils::column($result, 0), ArrayUtils::column($result, 1)); }
/** * Returns true if there is any entity with reference to the passed one. * * @param $entityName * @param $entityId * @return bool */ private function existsSomeEntityWithReferenceTo($entityName, $entityId) { $entityNames = $this->dbSchemaInfo->getAllEntityNames(); foreach ($entityNames as $otherEntityName) { $otherEntityInfo = $this->dbSchemaInfo->getEntityInfo($otherEntityName); $otherEntityReferences = $otherEntityInfo->references; $otherEntityMnReferences = $otherEntityInfo->mnReferences; $otherEntityValueReferences = $otherEntityInfo->valueReferences; $allReferences = array_merge($otherEntityReferences, $otherEntityMnReferences, $otherEntityValueReferences); foreach ($allReferences as $reference => $referencedEntity) { // if the target is dynamic, check it anyway - just to be sure if ($referencedEntity !== $entityName && $referencedEntity[0] !== '@') { continue; } $otherEntityStorage = $this->storageFactory->getStorage($otherEntityName); $possiblyReferencingEntities = $otherEntityStorage->loadAll(); if (isset($otherEntityReferences[$reference])) { // 1:N reference $vpReference = "vp_{$reference}"; foreach ($possiblyReferencingEntities as $possiblyReferencingEntity) { if (isset($possiblyReferencingEntity[$vpReference])) { $referencedVpidsString = $possiblyReferencingEntity[$vpReference]; preg_match_all(IdUtil::getRegexMatchingId(), $referencedVpidsString, $matches); if (ArrayUtils::any($matches[0], Comparators::equals($entityId))) { return true; } } } } elseif (isset($otherEntityMnReferences[$reference])) { // M:N reference $vpReference = "vp_{$otherEntityName}"; foreach ($possiblyReferencingEntities as $possiblyReferencingEntity) { if (isset($possiblyReferencingEntity[$vpReference]) && array_search($entityId, $possiblyReferencingEntity[$vpReference]) !== false) { return true; } } } elseif (isset($otherEntityValueReferences[$reference])) { // Value reference list($sourceColumn, $sourceValue, $valueColumn, $pathInStructure) = array_values(ReferenceUtils::getValueReferenceDetails($reference)); foreach ($possiblyReferencingEntities as $possiblyReferencingEntity) { if (isset($possiblyReferencingEntity[$sourceColumn]) && ($possiblyReferencingEntity[$sourceColumn] === $sourceValue || ReferenceUtils::valueMatchesWildcard($sourceValue, $possiblyReferencingEntity[$sourceColumn])) && isset($possiblyReferencingEntity[$valueColumn])) { if (is_numeric($possiblyReferencingEntity[$valueColumn]) && intval($possiblyReferencingEntity[$valueColumn]) === 0 || $possiblyReferencingEntity[$valueColumn] === '') { continue; } if ($pathInStructure) { $possiblyReferencingEntity[$valueColumn] = unserialize($possiblyReferencingEntity[$valueColumn]); $paths = ReferenceUtils::getMatchingPaths($possiblyReferencingEntity[$valueColumn], $pathInStructure); } else { $paths = [[]]; // root = the value itself } /** @var Cursor[] $cursors */ $cursors = array_map(function ($path) use(&$possiblyReferencingEntity, $valueColumn) { return new Cursor($possiblyReferencingEntity[$valueColumn], $path); }, $paths); foreach ($cursors as $cursor) { $vpidsString = $cursor->getValue(); preg_match_all(IdUtil::getRegexMatchingId(), $vpidsString, $matches); if (ArrayUtils::any($matches[0], Comparators::equals($entityId))) { return true; } } } } } } } return false; }
function sort($changeInfoList) { ArrayUtils::stablesort($changeInfoList, array($this, 'compareChangeInfo')); return $changeInfoList; }
/** * Creates manual commit. Adds everything to stage. * * @param WP_REST_Request $request * @return WP_REST_Response|WP_Error */ public function commit(WP_REST_Request $request) { $currentUser = wp_get_current_user(); if ($currentUser->ID === 0) { return new WP_Error('error', 'You don\'t have permission to do this.', ['status' => 403]); } /** @noinspection PhpUndefinedFieldInspection */ $authorName = $currentUser->display_name; /** @noinspection PhpUndefinedFieldInspection */ $authorEmail = $currentUser->user_email; $this->gitRepository->stageAll(); $status = $this->gitRepository->getStatus(true); if (ArrayUtils::any($status, function ($fileStatus) { $vpdbName = basename(VP_VPDB_DIR); return Strings::contains($fileStatus[1], $vpdbName); })) { $this->updateDatabase($status); } $commitMessage = new CommitMessage($request['commit-message']); $changeInfoEnvelope = new ChangeInfoEnvelope([new UntrackedChangeInfo($commitMessage)]); $this->gitRepository->commit($changeInfoEnvelope->getCommitMessage(), $authorName, $authorEmail); return new WP_REST_Response(true); }
/** * If entity type identified by $entityName defines an ID column, creates a mapping between WordPress ID and VPID * for all entities (db rows) of such type. * * @param string $entityName E.g., "post" */ private function createVpidsForEntitiesOfType($entityName) { if (!$this->dbSchema->getEntityInfo($entityName)->usesGeneratedVpids) { return; } $idColumnName = $this->dbSchema->getEntityInfo($entityName)->idColumnName; $tableName = $this->dbSchema->getTableName($entityName); $prefixedTableName = $this->dbSchema->getPrefixedTableName($entityName); $entities = $this->database->get_results("SELECT * FROM {$prefixedTableName}", ARRAY_A); $entities = $this->replaceForeignKeysWithReferencesInAllEntities($entityName, $entities); $storage = $this->storageFactory->getStorage($entityName); $entities = array_filter($entities, function ($entity) use($storage) { return $storage->shouldBeSaved($entity); }); $chunks = array_chunk($entities, 1000); foreach ($chunks as $entitiesInChunk) { $wordpressIds = array_column($entitiesInChunk, $idColumnName); $idPairs = []; foreach ($wordpressIds as $id) { $id = intval($id); if (!isset($this->idCache[$entityName], $this->idCache[$entityName][$id])) { $this->idCache[$entityName][$id] = IdUtil::newId(); } $idPairs[$id] = $this->idCache[$entityName][$id]; } $sqlValues = join(', ', ArrayUtils::map(function ($vpId, $id) use($tableName) { return "('{$tableName}', {$id}, UNHEX('{$vpId}'))"; }, $idPairs)); $query = "INSERT INTO {$this->database->vp_id} (`table`, id, vp_id) VALUES {$sqlValues}"; $this->database->query($query); $this->checkTimeout(); } }
/** * If entity type identified by $entityName defines an ID column, creates a mapping between WordPress ID and VPID * for all entities (db rows) of such type. * * @param string $entityName E.g., "post" */ private function createVpidsForEntitiesOfType($entityName) { if (!$this->dbSchema->getEntityInfo($entityName)->usesGeneratedVpids) { return; } $idColumnName = $this->dbSchema->getEntityInfo($entityName)->idColumnName; $tableName = $this->dbSchema->getTableName($entityName); $prefixedTableName = $this->dbSchema->getPrefixedTableName($entityName); $entities = $this->database->get_results("SELECT * FROM {$prefixedTableName}", ARRAY_A); $entities = $this->replaceForeignKeysWithReferencesInAllEntities($entityName, $entities); $storage = $this->storageFactory->getStorage($entityName); $entities = array_filter($entities, function ($entity) use($storage) { return $storage->shouldBeSaved($entity); }); $chunks = array_chunk($entities, 1000); $this->idCache[$entityName] = array(); foreach ($chunks as $entitiesInChunk) { $wordpressIds = ArrayUtils::column($entitiesInChunk, $idColumnName); $vpIds = array_map(array('VersionPress\\Utils\\IdUtil', 'newId'), $entitiesInChunk); $idPairs = array_combine($wordpressIds, $vpIds); $this->idCache[$entityName] = $this->idCache[$entityName] + $idPairs; // merge arrays with preserving keys $sqlValues = join(', ', ArrayUtils::map(function ($vpId, $id) use($tableName) { return "('{$tableName}', {$id}, UNHEX('{$vpId}'))"; }, $idPairs)); $query = "INSERT INTO {$this->getTableName('vp_id')} (`table`, id, vp_id) VALUES {$sqlValues}"; $this->database->query($query); $this->checkTimeout(); } }
/** * Creates manual commit. Adds everything to stage. * * @param WP_REST_Request $request * @return WP_REST_Response|\WP_Error */ public function commit(WP_REST_Request $request) { $currentUser = wp_get_current_user(); if ($currentUser->ID === 0) { return new \WP_Error('error', 'You don\'t have permission to do this.', array('status' => 403)); } /** @noinspection PhpUndefinedFieldInspection */ $authorName = $currentUser->display_name; /** @noinspection PhpUndefinedFieldInspection */ $authorEmail = $currentUser->user_email; $this->gitRepository->stageAll(); $status = $this->gitRepository->getStatus(true); if (ArrayUtils::any($status, function ($fileStatus) { return Strings::contains($fileStatus[1], 'vpdb'); })) { $this->updateDatabase($status); } $this->gitRepository->commit($request['commit-message'], $authorName, $authorEmail); return new WP_REST_Response(true); }
private function loadAllFromFiles($entityFiles) { $entities = array_map(array($this, 'deserializeEntity'), array_map('file_get_contents', $entityFiles)); $vpIds = ArrayUtils::column($entities, $this->entityInfo->vpidColumnName); return array_combine($vpIds, $entities); }
private function groupBulkActions($changeInfoList) { $bulkChangeInfoClasses = $this->bulkChangeInfoClasses; $groupedChangeInfos = ArrayUtils::mapreduce($changeInfoList, function (TrackedChangeInfo $item, $mapEmit) { $key = "{$item->getEntityName()}/{$item->getAction()}"; $mapEmit($key, $item); }, function ($key, $items, $reduceEmit) use($bulkChangeInfoClasses) { /** @var TrackedChangeInfo[] $items */ if (count($items) > 1) { $entityName = $items[0]->getEntityName(); if (isset($bulkChangeInfoClasses[$entityName])) { $reduceEmit(new $bulkChangeInfoClasses[$entityName]($items)); } else { $reduceEmit($items); } } else { $reduceEmit($items[0]); } }); $changeInfos = array(); foreach ($groupedChangeInfos as $changeInfoGroup) { if (is_array($changeInfoGroup)) { foreach ($changeInfoGroup as $changeInfo) { $changeInfos[] = $changeInfo; } } else { $changeInfos[] = $changeInfoGroup; } } return $changeInfos; }
private function groupBulkActions($changeInfoList) { $groupedChangeInfos = ArrayUtils::mapreduce($changeInfoList, function (ChangeInfo $item, $mapEmit) { if ($item instanceof TrackedChangeInfo) { $key = "{$item->getScope()}/{$item->getAction()}"; $mapEmit($key, $item); } else { $mapEmit(spl_object_hash($item), $item); } }, function ($key, $items, $reduceEmit) { /** @var TrackedChangeInfo[] $items */ if (count($items) > 1) { $reduceEmit(new BulkChangeInfo($items)); } else { $reduceEmit($items[0]); } }); $changeInfos = []; foreach ($groupedChangeInfos as $changeInfoGroup) { if (is_array($changeInfoGroup)) { foreach ($changeInfoGroup as $changeInfo) { $changeInfos[] = $changeInfo; } } else { $changeInfos[] = $changeInfoGroup; } } return $changeInfos; }
private function containsTermChangeInfo($changeInfoList) { return ArrayUtils::any($changeInfoList, function ($changeInfo) { return ChangeInfoUtils::changeInfoRepresentsEntity($changeInfo, 'term'); }); }
private static function getRelatedHooks($tag, $hookType) { $relatedHooks = isset(self::$hooks[$hookType][$tag]) ? self::$hooks[$hookType][$tag] : []; ArrayUtils::stablesort($relatedHooks, function ($hook1, $hook2) { return $hook1['priority'] - $hook2['priority']; }); return $relatedHooks; }
/** * @param $entityName */ private static function assertEntitiesEqualDatabase($entityName) { $storage = self::$storageFactory->getStorage($entityName); $entityInfo = self::$schemaInfo->getEntityInfo($entityName); $allDbEntities = self::selectAll(self::$schemaInfo->getPrefixedTableName($entityName)); $idMap = self::getVpIdMap(); $allDbEntities = self::identifyEntities($entityName, $allDbEntities, $idMap); $allDbEntities = self::replaceForeignKeys($entityName, $allDbEntities, $idMap); $dbEntities = array_filter($allDbEntities, [$storage, 'shouldBeSaved']); $urlReplacer = new AbsoluteUrlReplacer(self::$testConfig->testSite->url); $storageEntities = array_map(function ($entity) use($urlReplacer) { return $urlReplacer->restore($entity); }, $storage->loadAll()); $countOfentitiesInDb = count($dbEntities); $countOfentitiesInStorage = count($storageEntities); if ($countOfentitiesInDb !== $countOfentitiesInStorage) { if ($countOfentitiesInStorage > $countOfentitiesInDb) { $problematicEntities = self::findMissingEntities($entityName, $storageEntities, $dbEntities); } else { $problematicEntities = self::findExceedingEntities($entityName, $storageEntities, $dbEntities); } throw new \PHPUnit_Framework_AssertionFailedError("Different count of synchronized entities ({$entityName}): DB = {$countOfentitiesInDb}, " . "storage = {$countOfentitiesInStorage}\nProblematic entities: " . join(", ", $problematicEntities)); } foreach ($dbEntities as $dbEntity) { $id = $dbEntity[$entityInfo->vpidColumnName]; $storageEntity = $storageEntities[$id]; $dbEntity = self::$shortcodesReplacer->replaceShortcodesInEntity($entityName, $dbEntity); foreach ($dbEntity as $column => $value) { if ($entityInfo->idColumnName === $column || isset($entityInfo->getIgnoredColumns()[$column])) { continue; } if (!isset($storageEntity[$column])) { throw new \PHPUnit_Framework_AssertionFailedError("{$entityName}[{$column}] with value = {$value}, ID = {$id} not found in storage"); } if (is_string($storageEntity[$column])) { $storageEntity[$column] = str_replace("\r\n", "\n", $storageEntity[$column]); } if (is_string($value)) { $value = str_replace("\r\n", "\n", $value); } if ($storageEntity[$column] != $value) { throw new \PHPUnit_Framework_AssertionFailedError("Different values ({$entityName}[{$column}]: {$id}): DB = {$value}, storage = {$storageEntity[$column]}"); } } } $missingReferences = []; $exceedingReferences = []; foreach ($entityInfo->mnReferences as $reference => $targetEntity) { if ($entityInfo->isVirtualReference($reference)) { continue; } $referenceDetails = ReferenceUtils::getMnReferenceDetails(self::$schemaInfo, $entityName, $reference); $sourceColumn = $referenceDetails['source-column']; $targetColumn = $referenceDetails['target-column']; $junctionTable = $referenceDetails['junction-table']; $prefixedJunctionTable = self::$schemaInfo->getPrefixedTableName($junctionTable); $prefixedVpIdTable = self::$schemaInfo->getPrefixedTableName('vp_id'); $sourceTable = self::$schemaInfo->getTableName($referenceDetails['source-entity']); $targetTable = self::$schemaInfo->getTableName($referenceDetails['target-entity']); $junctionTableContent = self::fetchAll("SELECT HEX(s_vp_id.vp_id), HEX(t_vp_id.vp_id) FROM {$prefixedJunctionTable} j\n JOIN {$prefixedVpIdTable} s_vp_id ON j.{$sourceColumn} = s_vp_id.id AND s_vp_id.`table`='{$sourceTable}'\n JOIN {$prefixedVpIdTable} t_vp_id ON j.{$targetColumn} = t_vp_id.id AND t_vp_id.`table` = '{$targetTable}'", MYSQLI_NUM); $checkedReferences = []; $missingReferences[$junctionTable] = []; foreach ($storageEntities as $storageEntity) { if (!isset($storageEntity["vp_{$targetEntity}"])) { continue; } foreach ($storageEntity["vp_{$targetEntity}"] as $referenceVpId) { if (!ArrayUtils::any($junctionTableContent, function ($junctionRow) use($storageEntity, $referenceVpId) { return $junctionRow[0] === $storageEntity['vp_id'] && $junctionRow[1] === $referenceVpId; })) { $missingReferences[$junctionTable][] = [$sourceColumn => $storageEntity['vp_id'], $targetColumn => $referenceVpId]; } $checkedReferences[] = [$storageEntity['vp_id'], $referenceVpId]; } } $exceedingReferences[$junctionTable] = array_map(function ($pair) use($sourceColumn, $targetColumn) { return [$sourceColumn => $pair[0], $targetColumn => $pair[1]]; }, array_filter($junctionTableContent, function ($pair) use($checkedReferences) { foreach ($checkedReferences as $reference) { if ($reference[0] === $pair[0] && $reference[1] === $pair[1]) { return false; } } return true; })); } self::reportResultOfMnReferenceCheck($missingReferences, "Missing"); self::reportResultOfMnReferenceCheck($exceedingReferences, "Exceeding"); }