MOON
Server: Apache
System: Linux server1.studioinfinity.com.br 2.6.32-954.3.5.lve1.4.90.el6.x86_64 #1 SMP Tue Feb 21 12:26:30 UTC 2023 x86_64
User: artinside (517)
PHP: 7.4.33
Disabled: exec,passthru,shell_exec,system
Upload Files
File: /home/artinside/sites.artinside.com.br/paliar/vendor/aplus/database/src/Definition/AlterTable.php
<?php declare(strict_types=1);
/*
 * This file is part of Aplus Framework Database Library.
 *
 * (c) Natan Felles <natanfelles@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Framework\Database\Definition;

use Framework\Database\Definition\Table\TableDefinition;
use Framework\Database\Definition\Table\TableStatement;
use InvalidArgumentException;
use LogicException;

/**
 * Class AlterTable.
 *
 * @see https://mariadb.com/kb/en/alter-table/
 *
 * @package database
 */
class AlterTable extends TableStatement
{
    public const string ALGO_COPY = 'COPY';
    public const string ALGO_DEFAULT = 'DEFAULT';
    public const string ALGO_INPLACE = 'INPLACE';
    public const string ALGO_INSTANT = 'INSTANT';
    public const string ALGO_NOCOPY = 'NOCOPY';
    public const string LOCK_DEFAULT = 'DEFAULT';
    public const string LOCK_EXCLUSIVE = 'EXCLUSIVE';
    public const string LOCK_NONE = 'NONE';
    public const string LOCK_SHARED = 'SHARED';

    /**
     * @return static
     */
    public function online() : static
    {
        $this->sql['online'] = true;
        return $this;
    }

    protected function renderOnline() : ?string
    {
        if (!isset($this->sql['online'])) {
            return null;
        }
        return ' ONLINE';
    }

    /**
     * @return static
     */
    public function ignore() : static
    {
        $this->sql['ignore'] = true;
        return $this;
    }

    protected function renderIgnore() : ?string
    {
        if (!isset($this->sql['ignore'])) {
            return null;
        }
        return ' IGNORE';
    }

    public function ifExists() : static
    {
        $this->sql['if_exists'] = true;
        return $this;
    }

    protected function renderIfExists() : ?string
    {
        if (!isset($this->sql['if_exists'])) {
            return null;
        }
        return ' IF EXISTS';
    }

    /**
     * @param string $tableName
     *
     * @return static
     */
    public function table(string $tableName) : static
    {
        $this->sql['table'] = $tableName;
        return $this;
    }

    protected function renderTable() : string
    {
        if (isset($this->sql['table'])) {
            return ' ' . $this->database->protectIdentifier($this->sql['table']);
        }
        throw new LogicException('TABLE name must be set');
    }

    /**
     * @param int $seconds
     *
     * @return static
     */
    public function wait(int $seconds) : static
    {
        $this->sql['wait'] = $seconds;
        return $this;
    }

    protected function renderWait() : ?string
    {
        if (!isset($this->sql['wait'])) {
            return null;
        }
        if ($this->sql['wait'] < 0) {
            throw new InvalidArgumentException(
                "Invalid WAIT value: {$this->sql['wait']}"
            );
        }
        return " WAIT {$this->sql['wait']}";
    }

    public function noWait() : static
    {
        $this->sql['no_wait'] = true;
        return $this;
    }

    protected function renderNoWait() : ?string
    {
        if (!isset($this->sql['no_wait'])) {
            return null;
        }
        if (isset($this->sql['wait'])) {
            throw new LogicException('WAIT and NOWAIT can not be used together');
        }
        return ' NOWAIT';
    }

    /**
     * @param callable $definition
     * @param bool $ifNotExists
     *
     * @return static
     */
    public function add(callable $definition, bool $ifNotExists = false) : static
    {
        $this->sql['add'][] = [
            'definition' => $definition,
            'if_not_exists' => $ifNotExists,
        ];
        return $this;
    }

    /**
     * @param callable $definition
     *
     * @return static
     */
    public function addIfNotExists(callable $definition) : static
    {
        $this->sql['add'][] = [
            'definition' => $definition,
            'if_not_exists' => true,
        ];
        return $this;
    }

    protected function renderAdd() : ?string
    {
        if (!isset($this->sql['add'])) {
            return null;
        }
        $parts = [];
        foreach ($this->sql['add'] as $add) {
            $definition = new TableDefinition(
                $this->database,
                $add['if_not_exists'] ? 'IF NOT EXISTS' : null
            );
            $add['definition']($definition);
            $part = $definition->sql('ADD');
            if ($part) {
                $parts[] = $part;
            }
        }
        return $parts ? \implode(',' . \PHP_EOL, $parts) : null;
    }

    /**
     * @param callable $definition
     * @param bool $ifExists
     *
     * @return static
     */
    public function change(callable $definition, bool $ifExists = false) : static
    {
        $this->sql['change'][] = [
            'definition' => $definition,
            'if_exists' => $ifExists,
        ];
        return $this;
    }

    public function changeIfExists(callable $definition) : static
    {
        $this->sql['change'][] = [
            'definition' => $definition,
            'if_exists' => true,
        ];
        return $this;
    }

    protected function renderChange() : ?string
    {
        if (!isset($this->sql['change'])) {
            return null;
        }
        $parts = [];
        foreach ($this->sql['change'] as $change) {
            $definition = new TableDefinition(
                $this->database,
                $change['if_exists'] ? 'IF EXISTS' : null
            );
            $change['definition']($definition);
            $part = $definition->sql('CHANGE');
            if ($part) {
                $parts[] = $part;
            }
        }
        return $parts ? \implode(',' . \PHP_EOL, $parts) : null;
    }

    /**
     * @param callable $definition
     * @param bool $ifExists
     *
     * @return static
     */
    public function modify(callable $definition, bool $ifExists = false) : static
    {
        $this->sql['modify'][] = [
            'definition' => $definition,
            'if_exists' => $ifExists,
        ];
        return $this;
    }

    public function modifyIfExists(callable $definition) : static
    {
        $this->sql['modify'][] = [
            'definition' => $definition,
            'if_exists' => true,
        ];
        return $this;
    }

    protected function renderModify() : ?string
    {
        if (!isset($this->sql['modify'])) {
            return null;
        }
        $parts = [];
        foreach ($this->sql['modify'] as $modify) {
            $definition = new TableDefinition(
                $this->database,
                $modify['if_exists'] ? 'IF EXISTS' : null
            );
            $modify['definition']($definition);
            $part = $definition->sql('MODIFY');
            if ($part) {
                $parts[] = $part;
            }
        }
        return $parts ? \implode(',' . \PHP_EOL, $parts) : null;
    }

    public function dropColumn(string $name, bool $ifExists = false) : static
    {
        $this->sql['drop_columns'][$name] = $ifExists;
        return $this;
    }

    public function dropColumnIfExists(string $name) : static
    {
        $this->sql['drop_columns'][$name] = true;
        return $this;
    }

    protected function renderDropColumns() : ?string
    {
        if (!isset($this->sql['drop_columns'])) {
            return null;
        }
        $drops = [];
        foreach ($this->sql['drop_columns'] as $name => $ifExists) {
            $name = $this->database->protectIdentifier($name);
            $ifExists = $ifExists ? 'IF EXISTS ' : '';
            $drops[] = ' DROP COLUMN ' . $ifExists . $name;
        }
        return \implode(',' . \PHP_EOL, $drops);
    }

    public function dropPrimaryKey() : static
    {
        $this->sql['drop_primary_key'] = true;
        return $this;
    }

    protected function renderDropPrimaryKey() : ?string
    {
        if (!isset($this->sql['drop_primary_key'])) {
            return null;
        }
        return ' DROP PRIMARY KEY';
    }

    public function dropKey(string $name, bool $ifExists = false) : static
    {
        $this->sql['drop_keys'][$name] = $ifExists;
        return $this;
    }

    public function dropKeyIfExists(string $name) : static
    {
        $this->sql['drop_keys'][$name] = true;
        return $this;
    }

    protected function renderDropKeys() : ?string
    {
        if (!isset($this->sql['drop_keys'])) {
            return null;
        }
        $drops = [];
        foreach ($this->sql['drop_keys'] as $name => $ifExists) {
            $name = $this->database->protectIdentifier($name);
            $ifExists = $ifExists ? 'IF EXISTS ' : '';
            $drops[] = ' DROP KEY ' . $ifExists . $name;
        }
        return \implode(',' . \PHP_EOL, $drops);
    }

    public function dropForeignKey(string $name, bool $ifExists = false) : static
    {
        $this->sql['drop_foreign_keys'][$name] = $ifExists;
        return $this;
    }

    public function dropForeignKeyIfExists(string $name) : static
    {
        $this->sql['drop_foreign_keys'][$name] = true;
        return $this;
    }

    protected function renderDropForeignKeys() : ?string
    {
        if (!isset($this->sql['drop_foreign_keys'])) {
            return null;
        }
        $drops = [];
        foreach ($this->sql['drop_foreign_keys'] as $name => $ifExists) {
            $name = $this->database->protectIdentifier($name);
            $ifExists = $ifExists ? 'IF EXISTS ' : '';
            $drops[] = ' DROP FOREIGN KEY ' . $ifExists . $name;
        }
        return \implode(',' . \PHP_EOL, $drops);
    }

    public function dropConstraint(string $name, bool $ifExists = false) : static
    {
        $this->sql['drop_constraints'][$name] = $ifExists;
        return $this;
    }

    public function dropConstraintIfExists(string $name) : static
    {
        $this->sql['drop_constraints'][$name] = true;
        return $this;
    }

    protected function renderDropConstraints() : ?string
    {
        if (!isset($this->sql['drop_constraints'])) {
            return null;
        }
        $drops = [];
        foreach ($this->sql['drop_constraints'] as $name => $ifExists) {
            $name = $this->database->protectIdentifier($name);
            $ifExists = $ifExists ? 'IF EXISTS ' : '';
            $drops[] = ' DROP CONSTRAINT ' . $ifExists . $name;
        }
        return \implode(',' . \PHP_EOL, $drops);
    }

    public function disableKeys() : static
    {
        $this->sql['disable_keys'] = true;
        return $this;
    }

    protected function renderDisableKeys() : ?string
    {
        if (!isset($this->sql['disable_keys'])) {
            return null;
        }
        return ' DISABLE KEYS';
    }

    public function enableKeys() : static
    {
        $this->sql['enable_keys'] = true;
        return $this;
    }

    protected function renderEnableKeys() : ?string
    {
        if (!isset($this->sql['enable_keys'])) {
            return null;
        }
        return ' ENABLE KEYS';
    }

    public function renameTo(string $newTableName) : static
    {
        $this->sql['rename_to'] = $newTableName;
        return $this;
    }

    protected function renderRenameTo() : ?string
    {
        if (!isset($this->sql['rename_to'])) {
            return null;
        }
        return ' RENAME TO ' . $this->database->protectIdentifier($this->sql['rename_to']);
    }

    public function orderBy(string $column, string ...$columns) : static
    {
        foreach ([$column, ...$columns] as $col) {
            $this->sql['order_by'][] = $col;
        }
        return $this;
    }

    protected function renderOrderBy() : ?string
    {
        if (!isset($this->sql['order_by'])) {
            return null;
        }
        $columns = [];
        foreach ($this->sql['order_by'] as $column) {
            $columns[] = $this->database->protectIdentifier($column);
        }
        return ' ORDER BY ' . \implode(', ', $columns);
    }

    public function renameColumn(string $name, string $newName) : static
    {
        $this->sql['rename_columns'][$name] = $newName;
        return $this;
    }

    protected function renderRenameColumns() : ?string
    {
        if (!isset($this->sql['rename_columns'])) {
            return null;
        }
        $renames = [];
        foreach ($this->sql['rename_columns'] as $name => $newName) {
            $name = $this->database->protectIdentifier($name);
            $newName = $this->database->protectIdentifier($newName);
            $renames[] = ' RENAME COLUMN ' . $name . ' TO ' . $newName;
        }
        return \implode(',' . \PHP_EOL, $renames);
    }

    public function renameKey(string $name, string $newName) : static
    {
        $this->sql['rename_keys'][$name] = $newName;
        return $this;
    }

    protected function renderRenameKeys() : ?string
    {
        if (!isset($this->sql['rename_keys'])) {
            return null;
        }
        $renames = [];
        foreach ($this->sql['rename_keys'] as $name => $newName) {
            $name = $this->database->protectIdentifier($name);
            $newName = $this->database->protectIdentifier($newName);
            $renames[] = ' RENAME KEY ' . $name . ' TO ' . $newName;
        }
        return \implode(',' . \PHP_EOL, $renames);
    }

    public function convertToCharset(string $charset, ?string $collation = null) : static
    {
        $this->sql['convert_to_charset'] = [
            'charset' => $charset,
            'collation' => $collation,
        ];
        return $this;
    }

    protected function renderConvertToCharset() : ?string
    {
        if (!isset($this->sql['convert_to_charset'])) {
            return null;
        }
        $charset = $this->database->quote($this->sql['convert_to_charset']['charset']);
        $convert = ' CONVERT TO CHARACTER SET ' . $charset;
        if (isset($this->sql['convert_to_charset']['collation'])) {
            $convert .= ' COLLATE ' . $this->database->quote($this->sql['convert_to_charset']['collation']);
        }
        return $convert;
    }

    public function charset(?string $charset) : static
    {
        $this->sql['charset'] = $charset ?? 'DEFAULT';
        return $this;
    }

    protected function renderCharset() : ?string
    {
        if (!isset($this->sql['charset'])) {
            return null;
        }
        $charset = \strtolower($this->sql['charset']);
        if ($charset === 'default') {
            return ' DEFAULT CHARACTER SET';
        }
        return ' CHARACTER SET = ' . $this->database->quote($charset);
    }

    public function collate(?string $collation) : static
    {
        $this->sql['collate'] = $collation ?? 'DEFAULT';
        return $this;
    }

    protected function renderCollate() : ?string
    {
        if (!isset($this->sql['collate'])) {
            return null;
        }
        $collate = \strtolower($this->sql['collate']);
        if ($collate === 'default') {
            return ' DEFAULT COLLATE';
        }
        return ' COLLATE = ' . $this->database->quote($collate);
    }

    /**
     * @param string $type
     *
     * @see https://mariadb.com/kb/en/alter-table/#lock
     * @see AlterTable::LOCK_DEFAULT
     * @see AlterTable::LOCK_EXCLUSIVE
     * @see AlterTable::LOCK_NONE
     * @see AlterTable::LOCK_SHARED
     *
     * @return static
     */
    public function lock(string $type) : static
    {
        $this->sql['lock'] = $type;
        return $this;
    }

    protected function renderLock() : ?string
    {
        if (!isset($this->sql['lock'])) {
            return null;
        }
        $lock = \strtoupper($this->sql['lock']);
        if (!\in_array($lock, [
            static::LOCK_DEFAULT,
            static::LOCK_EXCLUSIVE,
            static::LOCK_NONE,
            static::LOCK_SHARED,
        ], true)) {
            throw new InvalidArgumentException("Invalid LOCK value: {$this->sql['lock']}");
        }
        return ' LOCK = ' . $lock;
    }

    public function force() : static
    {
        $this->sql['force'] = true;
        return $this;
    }

    protected function renderForce() : ?string
    {
        if (!isset($this->sql['force'])) {
            return null;
        }
        return ' FORCE';
    }

    /**
     * @param string $algo
     *
     * @see https://mariadb.com/kb/en/innodb-online-ddl-overview/#algorithm
     * @see AlterTable::ALGO_COPY
     * @see AlterTable::ALGO_DEFAULT
     * @see AlterTable::ALGO_INPLACE
     * @see AlterTable::ALGO_INSTANT
     * @see AlterTable::ALGO_NOCOPY
     *
     * @return static
     */
    public function algorithm(string $algo) : static
    {
        $this->sql['algorithm'] = $algo;
        return $this;
    }

    protected function renderAlgorithm() : ?string
    {
        if (!isset($this->sql['algorithm'])) {
            return null;
        }
        $algo = \strtoupper($this->sql['algorithm']);
        if (!\in_array($algo, [
            static::ALGO_COPY,
            static::ALGO_DEFAULT,
            static::ALGO_INPLACE,
            static::ALGO_INSTANT,
            static::ALGO_NOCOPY,
        ], true)) {
            throw new InvalidArgumentException("Invalid ALGORITHM value: {$this->sql['algorithm']}");
        }
        return ' ALGORITHM = ' . $algo;
    }

    public function sql() : string
    {
        $sql = 'ALTER' . $this->renderOnline() . $this->renderIgnore();
        $sql .= ' TABLE' . $this->renderIfExists();
        $sql .= $this->renderTable() . \PHP_EOL;
        $part = $this->renderWait() . $this->renderNoWait();
        if ($part) {
            $sql .= $part . \PHP_EOL;
        }
        $sql .= $this->joinParts([
            $this->renderOptions(),
            $this->renderAdd(),
            $this->renderChange(),
            $this->renderModify(),
            $this->renderDropColumns(),
            $this->renderDropPrimaryKey(),
            $this->renderDropKeys(),
            $this->renderDropForeignKeys(),
            $this->renderDropConstraints(),
            $this->renderDisableKeys(),
            $this->renderEnableKeys(),
            $this->renderRenameTo(),
            $this->renderOrderBy(),
            $this->renderRenameColumns(),
            $this->renderRenameKeys(),
            $this->renderConvertToCharset(),
            $this->renderCharset(),
            $this->renderCollate(),
            $this->renderAlgorithm(),
            $this->renderLock(),
            $this->renderForce(),
        ]);
        return $sql;
    }

    /**
     * @param array<string|null> $parts
     *
     * @return string
     */
    protected function joinParts(array $parts) : string
    {
        $result = '';
        $hasBefore = false;
        foreach ($parts as $part) {
            if ($part !== null) {
                $result .= $hasBefore ? ',' . \PHP_EOL : '';
                $result .= $part;
                $hasBefore = true;
            }
        }
        return $result;
    }

    /**
     * Runs the ALTER TABLE statement.
     *
     * @return int|string The number of affected rows
     */
    public function run() : int | string
    {
        return $this->database->exec($this->sql());
    }
}