123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- <?php
- namespace Dcat\Admin\Extend;
- use Carbon\Carbon;
- use Dcat\Admin\Models\Extension;
- use Dcat\Admin\Models\ExtensionHistory;
- use Dcat\Admin\Support\DatabaseUpdater;
- use Illuminate\Support\Arr;
- /**
- * Class VersionManager.
- *
- * @see https://github.com/octobercms/october/blob/develop/modules/system/classes/VersionManager.php
- */
- class VersionManager
- {
- use Note;
- const NO_VERSION_VALUE = 0;
- const HISTORY_TYPE_COMMENT = 1;
- const HISTORY_TYPE_SCRIPT = 2;
- protected $fileVersions;
- protected $databaseVersions;
- protected $databaseHistory;
- protected $updater;
- protected $manager;
- public function __construct(Manager $manager)
- {
- $this->manager = $manager;
- $this->updater = new DatabaseUpdater();
- }
- public function update($extension, $stopOnVersion = null)
- {
- $name = $this->manager->getName($extension);
- if (! $this->hasVersionFile($name)) {
- return false;
- }
- $currentVersion = $this->getLatestFileVersion($name);
- $databaseVersion = $this->getDatabaseVersion($name);
- if ($currentVersion === $databaseVersion) {
- $this->note('- <info>Nothing to update.</info>');
- return;
- }
- $this->manager->get($extension)->update($databaseVersion, $stopOnVersion ?: $currentVersion);
- $newUpdates = $this->getNewFileVersions($name, $databaseVersion);
- foreach ($newUpdates as $version => $details) {
- $this->applyExtensionUpdate($name, $version, $details);
- if ($stopOnVersion === $version) {
- return true;
- }
- }
- return true;
- }
- public function listNewVersions($extension)
- {
- $name = $this->manager->getName($extension);
- if (! $this->hasVersionFile($name)) {
- return [];
- }
- return $this->getNewFileVersions($name, $this->getDatabaseVersion($name));
- }
- protected function applyExtensionUpdate($name, $version, $details)
- {
- [$comments, $scripts] = $this->extractScriptsAndComments($details);
- foreach ($scripts as $script) {
- if ($this->hasDatabaseHistory($name, $version, $script)) {
- continue;
- }
- $this->applyDatabaseScript($name, $version, $script);
- }
- if (! $this->hasDatabaseHistory($name, $version)) {
- foreach ($comments as $comment) {
- $this->applyDatabaseComment($name, $version, $comment);
- $this->note(sprintf('- <info>v%s: </info> %s', $version, $comment));
- }
- }
- $this->setDatabaseVersion($name, $version);
- }
- public function remove($extension, $stopOnVersion = null, $stopCurrentVersion = false)
- {
- $name = $this->manager->getName($extension);
- if (! $this->hasVersionFile($name)) {
- return false;
- }
- $extensionHistory = $this->getDatabaseHistory($name);
- $extensionHistory = array_reverse($extensionHistory);
- $stopOnNextVersion = false;
- $newExtensionVersion = null;
- try {
- foreach ($extensionHistory as $history) {
- if ($stopCurrentVersion && $stopOnVersion === $history->version) {
- $newExtensionVersion = $history->version;
- break;
- }
- if ($stopOnNextVersion && $history->version !== $stopOnVersion) {
- $newExtensionVersion = $history->version;
- break;
- }
- if ($history->type == static::HISTORY_TYPE_COMMENT) {
- $this->removeDatabaseComment($name, $history->version);
- } elseif ($history->type == static::HISTORY_TYPE_SCRIPT) {
- $this->removeDatabaseScript($name, $history->version, $history->detail);
- }
- if ($stopOnVersion === $history->version) {
- $stopOnNextVersion = true;
- }
- }
- } catch (\Throwable $exception) {
- $lastHistory = $this->getLastHistory($name);
- if ($lastHistory) {
- $this->setDatabaseVersion($name, $lastHistory->version);
- }
- throw $exception;
- }
- $this->setDatabaseVersion($name, $newExtensionVersion);
- if (isset($this->fileVersions[$name])) {
- unset($this->fileVersions[$name]);
- }
- if (isset($this->databaseVersions[$name])) {
- unset($this->databaseVersions[$name]);
- }
- if (isset($this->databaseHistory[$name])) {
- unset($this->databaseHistory[$name]);
- }
- return true;
- }
- public function purge($name)
- {
- $name = $this->manager->getName($name);
- $versions = Extension::query()->where('name', $name);
- if ($countVersions = $versions->count()) {
- $versions->delete();
- }
- $history = ExtensionHistory::query()->where('name', $name);
- if ($countHistory = $history->count()) {
- $history->delete();
- }
- return $countHistory + $countVersions;
- }
- protected function getLatestFileVersion($name)
- {
- $versionInfo = $this->getFileVersions($name);
- if (! $versionInfo) {
- return static::NO_VERSION_VALUE;
- }
- return trim(key(array_slice($versionInfo, -1, 1)));
- }
- public function getNewFileVersions($name, $version = null)
- {
- $name = $this->manager->getName($name);
- if ($version === null) {
- $version = static::NO_VERSION_VALUE;
- }
- $versions = $this->getFileVersions($name);
- $position = array_search($version, array_keys($versions));
- return array_slice($versions, ++$position);
- }
- public function getFileVersions($name)
- {
- $name = $this->manager->getName($name);
- if ($this->fileVersions !== null && array_key_exists($name, $this->fileVersions)) {
- return $this->fileVersions[$name];
- }
- $versionInfo = (array) $this->parseVersionFile($this->getVersionFile($name));
- if ($versionInfo) {
- uksort($versionInfo, function ($a, $b) {
- return version_compare($a, $b);
- });
- }
- return $this->fileVersions[$name] = $versionInfo;
- }
- protected function parseVersionFile($file)
- {
- if ($file && is_file($file)) {
- return include $file;
- }
- }
- protected function getVersionFile($name)
- {
- return $this->manager->path($name, 'version.php');
- }
- protected function hasVersionFile($name)
- {
- $versionFile = $this->getVersionFile($name);
- return $versionFile && is_file($versionFile);
- }
- protected function getDatabaseVersion($name)
- {
- if ($this->databaseVersions === null) {
- $this->databaseVersions = Extension::query()->pluck('version', 'name');
- }
- if (! isset($this->databaseVersions[$name])) {
- $this->databaseVersions[$name] =
- Extension::query()
- ->where('name', $name)
- ->value('version');
- }
- return $this->databaseVersions[$name] ?? static::NO_VERSION_VALUE;
- }
- protected function setDatabaseVersion($name, $version = null)
- {
- $currentVersion = $this->getDatabaseVersion($name);
- if ($version && ! $currentVersion) {
- Extension::query()->create([
- 'name' => $name,
- 'version' => $version,
- ]);
- } elseif ($version && $currentVersion) {
- Extension::query()->where('name', $name)->update([
- 'version' => $version,
- 'updated_at' => new Carbon,
- ]);
- } elseif ($currentVersion) {
- Extension::query()->where('name', $name)->delete();
- }
- $this->databaseVersions[$name] = $version;
- }
- protected function applyDatabaseComment($name, $version, $comment)
- {
- ExtensionHistory::query()->create([
- 'name' => $name,
- 'type' => static::HISTORY_TYPE_COMMENT,
- 'version' => $version,
- 'detail' => $comment,
- ]);
- }
- protected function removeDatabaseComment($name, $version)
- {
- ExtensionHistory::query()
- ->where('name', $name)
- ->where('type', static::HISTORY_TYPE_COMMENT)
- ->where('version', $version)
- ->delete();
- }
- protected function applyDatabaseScript($name, $version, $script)
- {
- $updateFile = $this->manager->path($name, 'updates/'.$script);
- if (! is_file($updateFile)) {
- $this->note(sprintf('- <error>v%s: Migration file "%s" not found</error>', $version, $script));
- return;
- }
- $this->updater->setUp($this->resolveUpdater($name, $updateFile), function () use ($name, $version, $script) {
- ExtensionHistory::query()->create([
- 'name' => $name,
- 'type' => static::HISTORY_TYPE_SCRIPT,
- 'version' => $version,
- 'detail' => $script,
- ]);
- });
- $this->note(sprintf('- <info>v%s: Migrated</info> %s', $version, $script));
- }
- protected function resolveUpdater($name, $updateFile)
- {
- $updater = $this->updater->resolve($updateFile);
- if (method_exists($updater, 'setExtension')) {
- $updater->setExtension($this->manager->get($name));
- }
- return $updater;
- }
- protected function removeDatabaseScript($name, $version, $script)
- {
- $updateFile = $this->manager->path($name, 'updates/'.$script);
- $this->updater->packDown($this->resolveUpdater($name, $updateFile), function () use ($name, $version, $script) {
- ExtensionHistory::query()
- ->where('name', $name)
- ->where('type', static::HISTORY_TYPE_SCRIPT)
- ->where('version', $version)
- ->where('detail', $script)
- ->delete();
- });
- }
- protected function getDatabaseHistory($name)
- {
- if ($this->databaseHistory !== null && array_key_exists($name, $this->databaseHistory)) {
- return $this->databaseHistory[$name];
- }
- $historyInfo = ExtensionHistory::query()
- ->where('name', $name)
- ->orderBy('id')
- ->get()
- ->all();
- return $this->databaseHistory[$name] = $historyInfo;
- }
- protected function getLastHistory($name)
- {
- return ExtensionHistory::query()
- ->where('name', $name)
- ->orderByDesc('id')
- ->first();
- }
- protected function hasDatabaseHistory($name, $version, $script = null)
- {
- $historyInfo = $this->getDatabaseHistory($name);
- if (! $historyInfo) {
- return false;
- }
- foreach ($historyInfo as $history) {
- if ($history->version != $version) {
- continue;
- }
- if ($history->type == static::HISTORY_TYPE_COMMENT && ! $script) {
- return true;
- }
- if ($history->type == static::HISTORY_TYPE_SCRIPT && $history->detail == $script) {
- return true;
- }
- }
- return false;
- }
- protected function extractScriptsAndComments($details): array
- {
- $details = (array) $details;
- $fileNamePattern = "/^[a-z0-9\_\-\.\/\\\]+\.php$/i";
- $comments = array_values(array_filter($details, function ($detail) use ($fileNamePattern) {
- return ! preg_match($fileNamePattern, $detail);
- }));
- $scripts = array_values(array_filter($details, function ($detail) use ($fileNamePattern) {
- return preg_match($fileNamePattern, $detail);
- }));
- return [$comments, $scripts];
- }
- public function getCurrentVersion($extension): string
- {
- return $this->getDatabaseVersion($this->manager->getName($extension));
- }
- public function hasDatabaseVersion($extension, string $version): bool
- {
- $name = $this->manager->getName($extension);
- $histories = $this->getDatabaseHistory($name);
- foreach ($histories as $history) {
- if ($history->version === $version) {
- return true;
- }
- }
- return false;
- }
- public function getCurrentVersionNote($extension): string
- {
- $name = $this->manager->getName($extension);
- $histories = $this->getDatabaseHistory($name);
- $lastHistory = Arr::last(Arr::where($histories, function ($history) {
- return $history->type === static::HISTORY_TYPE_COMMENT;
- }));
- return $lastHistory ? $lastHistory->detail : '';
- }
- }
|