├── src ├── Definition │ ├── Table │ │ ├── Indexes │ │ │ ├── Keys │ │ │ │ ├── PrimaryKey.php │ │ │ │ ├── Key.php │ │ │ │ ├── UniqueKey.php │ │ │ │ ├── SpatialKey.php │ │ │ │ ├── FulltextKey.php │ │ │ │ ├── ConstraintKey.php │ │ │ │ └── ForeignKey.php │ │ │ ├── Index.php │ │ │ └── IndexDefinition.php │ │ ├── Columns │ │ │ ├── Numeric │ │ │ │ ├── BigintColumn.php │ │ │ │ ├── IntColumn.php │ │ │ │ ├── TinyintColumn.php │ │ │ │ ├── BooleanColumn.php │ │ │ │ ├── BitColumn.php │ │ │ │ ├── SmallintColumn.php │ │ │ │ ├── MediumintColumn.php │ │ │ │ ├── FloatColumn.php │ │ │ │ ├── DecimalColumn.php │ │ │ │ └── NumericDataType.php │ │ │ ├── String │ │ │ │ ├── JsonColumn.php │ │ │ │ ├── LongtextColumn.php │ │ │ │ ├── TinytextColumn.php │ │ │ │ ├── MediumtextColumn.php │ │ │ │ ├── VarcharColumn.php │ │ │ │ ├── BlobColumn.php │ │ │ │ ├── CharColumn.php │ │ │ │ ├── LongblobColumn.php │ │ │ │ ├── TextColumn.php │ │ │ │ ├── TinyblobColumn.php │ │ │ │ ├── MediumblobColumn.php │ │ │ │ ├── EnumColumn.php │ │ │ │ ├── SetColumn.php │ │ │ │ ├── VarbinaryColumn.php │ │ │ │ ├── BinaryColumn.php │ │ │ │ └── StringDataType.php │ │ │ ├── DateTime │ │ │ │ ├── DateColumn.php │ │ │ │ ├── TimeColumn.php │ │ │ │ ├── YearColumn.php │ │ │ │ ├── DatetimeColumn.php │ │ │ │ └── TimestampColumn.php │ │ │ ├── Geometry │ │ │ │ ├── PointColumn.php │ │ │ │ ├── PolygonColumn.php │ │ │ │ ├── GeometryColumn.php │ │ │ │ ├── LinestringColumn.php │ │ │ │ ├── MultipointColumn.php │ │ │ │ ├── MultipolygonColumn.php │ │ │ │ ├── MultilinestringColumn.php │ │ │ │ └── GeometryCollectionColumn.php │ │ │ ├── Traits │ │ │ │ ├── ListLength.php │ │ │ │ └── DecimalLength.php │ │ │ ├── Column.php │ │ │ └── ColumnDefinition.php │ │ ├── DefinitionPart.php │ │ ├── Constraint.php │ │ ├── Check.php │ │ ├── TableDefinition.php │ │ └── TableStatement.php │ ├── DropSchema.php │ ├── CreateSchema.php │ ├── CreateTable.php │ ├── DropTable.php │ └── AlterSchema.php ├── Manipulation │ ├── Traits │ │ ├── Select.php │ │ ├── Values.php │ │ ├── Set.php │ │ ├── OrderBy.php │ │ └── GroupBy.php │ ├── With.php │ ├── Update.php │ ├── Delete.php │ ├── Replace.php │ ├── Statement.php │ ├── Insert.php │ └── LoadData.php ├── Statement.php ├── PreparedStatement.php ├── Debug │ └── DatabaseCollector.php ├── Result.php └── Result │ └── Field.php ├── README.md ├── LICENSE ├── composer.json └── .phpstorm.meta.php /src/Definition/Table/Indexes/Keys/PrimaryKey.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Indexes\Keys; 11 | 12 | /** 13 | * Class PrimaryKey. 14 | * 15 | * @package database 16 | */ 17 | final class PrimaryKey extends ConstraintKey 18 | { 19 | protected string $type = 'PRIMARY KEY'; 20 | } 21 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Numeric/BigintColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Numeric; 11 | 12 | /** 13 | * Class BigintColumn. 14 | * 15 | * @package database 16 | */ 17 | final class BigintColumn extends NumericDataType 18 | { 19 | protected string $type = 'bigint'; 20 | } 21 | -------------------------------------------------------------------------------- /src/Definition/Table/Indexes/Keys/Key.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Indexes\Keys; 11 | 12 | use Framework\Database\Definition\Table\Indexes\Index; 13 | 14 | /** 15 | * Class Key. 16 | * 17 | * @package database 18 | */ 19 | final class Key extends Index 20 | { 21 | protected string $type = 'KEY'; 22 | } 23 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Numeric/IntColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Numeric; 11 | 12 | /** 13 | * Class IntColumn. 14 | * 15 | * @package database 16 | */ 17 | final class IntColumn extends NumericDataType 18 | { 19 | protected string $type = 'int'; 20 | protected int $maxLength = 11; 21 | } 22 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Numeric/TinyintColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Numeric; 11 | 12 | /** 13 | * Class TinyintColumn. 14 | * 15 | * @package database 16 | */ 17 | final class TinyintColumn extends NumericDataType 18 | { 19 | protected string $type = 'tinyint'; 20 | protected int $maxLength = 127; 21 | } 22 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/JsonColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | /** 13 | * Class JsonColumn. 14 | * 15 | * @see https://mariadb.com/kb/en/json-data-type/ 16 | * 17 | * @package database 18 | */ 19 | final class JsonColumn extends StringDataType 20 | { 21 | protected string $type = 'json'; 22 | } 23 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Numeric/BooleanColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Numeric; 11 | 12 | /** 13 | * Class BooleanColumn. 14 | * 15 | * @see https://mariadb.com/kb/en/boolean/ 16 | * 17 | * @package database 18 | */ 19 | final class BooleanColumn extends NumericDataType 20 | { 21 | protected string $type = 'boolean'; 22 | } 23 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/LongtextColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | /** 13 | * Class LongtextColumn. 14 | * 15 | * @see https://mariadb.com/kb/en/longtext/ 16 | * 17 | * @package database 18 | */ 19 | final class LongtextColumn extends StringDataType 20 | { 21 | protected string $type = 'longtext'; 22 | } 23 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/TinytextColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | /** 13 | * Class TinytextColumn. 14 | * 15 | * @see https://mariadb.com/kb/en/tinytext/ 16 | * 17 | * @package database 18 | */ 19 | final class TinytextColumn extends StringDataType 20 | { 21 | protected string $type = 'tinytext'; 22 | } 23 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/MediumtextColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | /** 13 | * Class MediumtextColumn. 14 | * 15 | * @see https://mariadb.com/kb/en/mediumtext/ 16 | * 17 | * @package database 18 | */ 19 | final class MediumtextColumn extends StringDataType 20 | { 21 | protected string $type = 'mediumtext'; 22 | } 23 | -------------------------------------------------------------------------------- /src/Definition/Table/Indexes/Keys/UniqueKey.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Indexes\Keys; 11 | 12 | /** 13 | * Class UniqueKey. 14 | * 15 | * @see https://mariadb.com/kb/en/getting-started-with-indexes/#unique-index 16 | * 17 | * @package database 18 | */ 19 | final class UniqueKey extends ConstraintKey 20 | { 21 | protected string $type = 'UNIQUE KEY'; 22 | } 23 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Numeric/BitColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Numeric; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class BitColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/bit/ 18 | * 19 | * @package database 20 | */ 21 | final class BitColumn extends Column 22 | { 23 | protected string $type = 'bit'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/VarcharColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | /** 13 | * Class VarcharColumn. 14 | * 15 | * @see https://mariadb.com/kb/en/varchar/ 16 | * 17 | * @package database 18 | */ 19 | final class VarcharColumn extends StringDataType 20 | { 21 | protected string $type = 'varchar'; 22 | protected int $maxLength = 65535; 23 | } 24 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/DateTime/DateColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\DateTime; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class EnumColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/date/ 18 | * 19 | * @package database 20 | */ 21 | final class DateColumn extends Column 22 | { 23 | protected string $type = 'date'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/DateTime/TimeColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\DateTime; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class TimeColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/time/ 18 | * 19 | * @package database 20 | */ 21 | final class TimeColumn extends Column 22 | { 23 | protected string $type = 'time'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/BlobColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class BlobColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/blob/ 18 | * 19 | * @package database 20 | */ 21 | final class BlobColumn extends Column 22 | { 23 | protected string $type = 'blob'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Geometry/PointColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Geometry; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class PointColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/point/ 18 | * 19 | * @package database 20 | */ 21 | final class PointColumn extends Column 22 | { 23 | protected string $type = 'point'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Numeric/SmallintColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Numeric; 11 | 12 | /** 13 | * Class SmallintColumn. 14 | * 15 | * @see https://mariadb.com/kb/en/smallint/ 16 | * 17 | * @package database 18 | */ 19 | final class SmallintColumn extends NumericDataType 20 | { 21 | protected string $type = 'smallint'; 22 | protected int $maxLength = 127; 23 | } 24 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/DateTime/YearColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\DateTime; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class YearColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/year-data-type/ 18 | * 19 | * @package database 20 | */ 21 | final class YearColumn extends Column 22 | { 23 | protected string $type = 'year'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Numeric/MediumintColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Numeric; 11 | 12 | /** 13 | * Class MediumintColumn. 14 | * 15 | * @see https://mariadb.com/kb/en/mediumint/ 16 | * 17 | * @package database 18 | */ 19 | final class MediumintColumn extends NumericDataType 20 | { 21 | protected string $type = 'mediumint'; 22 | protected int $maxLength = 127; 23 | } 24 | -------------------------------------------------------------------------------- /src/Definition/Table/Indexes/Keys/SpatialKey.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Indexes\Keys; 11 | 12 | use Framework\Database\Definition\Table\Indexes\Index; 13 | 14 | /** 15 | * Class SpatialKey. 16 | * 17 | * @see https://mariadb.com/kb/en/spatial-index/ 18 | * 19 | * @package database 20 | */ 21 | final class SpatialKey extends Index 22 | { 23 | protected string $type = 'SPATIAL KEY'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Geometry/PolygonColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Geometry; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class PolygonColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/polygon/ 18 | * 19 | * @package database 20 | */ 21 | final class PolygonColumn extends Column 22 | { 23 | protected string $type = 'polygon'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/DateTime/DatetimeColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\DateTime; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class DatetimeColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/datetime/ 18 | * 19 | * @package database 20 | */ 21 | final class DatetimeColumn extends Column 22 | { 23 | protected string $type = 'datetime'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Geometry/GeometryColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Geometry; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class GeometryColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/geometry/ 18 | * 19 | * @package database 20 | */ 21 | final class GeometryColumn extends Column 22 | { 23 | protected string $type = 'geometry'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/CharColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | /** 13 | * Class CharColumn. 14 | * 15 | * @see https://mariadb.com/kb/en/char/ 16 | * 17 | * @package database 18 | */ 19 | final class CharColumn extends StringDataType 20 | { 21 | protected string $type = 'char'; 22 | protected int $minLength = 0; 23 | protected int $maxLength = 255; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/LongblobColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class LongblobColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/longblob/ 18 | * 19 | * @package database 20 | */ 21 | final class LongblobColumn extends Column 22 | { 23 | protected string $type = 'longblob'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/TextColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | /** 13 | * Class TextColumn. 14 | * 15 | * @see https://mariadb.com/kb/en/text/ 16 | * 17 | * @package database 18 | */ 19 | final class TextColumn extends StringDataType 20 | { 21 | protected string $type = 'text'; 22 | protected int $minLength = 0; 23 | protected int $maxLength = 65535; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/TinyblobColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class TinyblobColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/tinyblob/ 18 | * 19 | * @package database 20 | */ 21 | final class TinyblobColumn extends Column 22 | { 23 | protected string $type = 'tinyblob'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/DateTime/TimestampColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\DateTime; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class TimestampColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/timestamp/ 18 | * 19 | * @package database 20 | */ 21 | final class TimestampColumn extends Column 22 | { 23 | protected string $type = 'timestamp'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Indexes/Keys/FulltextKey.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Indexes\Keys; 11 | 12 | use Framework\Database\Definition\Table\Indexes\Index; 13 | 14 | /** 15 | * Class FulltextKey. 16 | * 17 | * @see https://mariadb.com/kb/en/full-text-index-overview/ 18 | * 19 | * @package database 20 | */ 21 | final class FulltextKey extends Index 22 | { 23 | protected string $type = 'FULLTEXT KEY'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Geometry/LinestringColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Geometry; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class LinestringColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/linestring/ 18 | * 19 | * @package database 20 | */ 21 | final class LinestringColumn extends Column 22 | { 23 | protected string $type = 'linestring'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Geometry/MultipointColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Geometry; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class MultipointColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/multipoint/ 18 | * 19 | * @package database 20 | */ 21 | final class MultipointColumn extends Column 22 | { 23 | protected string $type = 'multipoint'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/MediumblobColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class MediumblobColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/mediumblob/ 18 | * 19 | * @package database 20 | */ 21 | final class MediumblobColumn extends Column 22 | { 23 | protected string $type = 'mediumblob'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Geometry/MultipolygonColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Geometry; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class MultipolygonColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/multipolygon/ 18 | * 19 | * @package database 20 | */ 21 | final class MultipolygonColumn extends Column 22 | { 23 | protected string $type = 'multipolygon'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/EnumColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | use Framework\Database\Definition\Table\Columns\Traits\ListLength; 13 | 14 | /** 15 | * Class EnumColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/enum/ 18 | * 19 | * @package database 20 | */ 21 | final class EnumColumn extends StringDataType 22 | { 23 | use ListLength; 24 | protected string $type = 'enum'; 25 | } 26 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/SetColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | use Framework\Database\Definition\Table\Columns\Traits\ListLength; 13 | 14 | /** 15 | * Class SetColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/set-data-type/ 18 | * 19 | * @package database 20 | */ 21 | final class SetColumn extends StringDataType 22 | { 23 | use ListLength; 24 | protected string $type = 'set'; 25 | } 26 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Geometry/MultilinestringColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Geometry; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class MultilinestringColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/multilinestring/ 18 | * 19 | * @package database 20 | */ 21 | final class MultilinestringColumn extends Column 22 | { 23 | protected string $type = 'multilinestring'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/VarbinaryColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class VarbinaryColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/varbinary/ 18 | * 19 | * @package database 20 | */ 21 | final class VarbinaryColumn extends Column 22 | { 23 | protected string $type = 'varbinary'; 24 | protected int $maxLength = 65535; 25 | } 26 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Geometry/GeometryCollectionColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Geometry; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class GeometryCollectionColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/geometrycollection/ 18 | * 19 | * @package database 20 | */ 21 | final class GeometryCollectionColumn extends Column 22 | { 23 | protected string $type = 'geometrycollection'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Numeric/FloatColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Numeric; 11 | 12 | use Framework\Database\Definition\Table\Columns\Traits\DecimalLength; 13 | 14 | /** 15 | * Class FloatColumn. 16 | * 17 | * @package database 18 | */ 19 | final class FloatColumn extends NumericDataType 20 | { 21 | use DecimalLength; 22 | protected string $type = 'float'; 23 | protected int $maxLength = 11; 24 | protected float $decimal; 25 | } 26 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Numeric/DecimalColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Numeric; 11 | 12 | use Framework\Database\Definition\Table\Columns\Traits\DecimalLength; 13 | 14 | /** 15 | * Class DecimalColumn. 16 | * 17 | * @package database 18 | */ 19 | final class DecimalColumn extends NumericDataType 20 | { 21 | use DecimalLength; 22 | protected string $type = 'decimal'; 23 | protected int $maxLength = 11; 24 | protected float $decimal; 25 | } 26 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/BinaryColumn.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class BinaryColumn. 16 | * 17 | * @see https://mariadb.com/kb/en/binary/ 18 | * 19 | * @package database 20 | */ 21 | final class BinaryColumn extends Column 22 | { 23 | protected string $type = 'binary'; 24 | protected int $minLength = 0; 25 | protected int $maxLength = 255; 26 | } 27 | -------------------------------------------------------------------------------- /src/Definition/Table/Indexes/Keys/ConstraintKey.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Indexes\Keys; 11 | 12 | use Framework\Database\Definition\Table\Constraint; 13 | use Framework\Database\Definition\Table\Indexes\Index; 14 | 15 | /** 16 | * Class ConstraintKey. 17 | * 18 | * @package database 19 | */ 20 | abstract class ConstraintKey extends Index 21 | { 22 | use Constraint; 23 | 24 | protected function renderType() : string 25 | { 26 | return $this->renderConstraint() . parent::renderType(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Traits/ListLength.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Traits; 11 | 12 | /** 13 | * Trait ListLength. 14 | * 15 | * @package database 16 | */ 17 | trait ListLength 18 | { 19 | protected function renderLength() : ?string 20 | { 21 | if (empty($this->length)) { 22 | return null; 23 | } 24 | $values = []; 25 | foreach ($this->length as $length) { 26 | $values[] = $this->database->quote($length); 27 | } 28 | $values = \implode(', ', $values); 29 | return "({$values})"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Traits/DecimalLength.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Traits; 11 | 12 | /** 13 | * Trait DecimalLength. 14 | * 15 | * @package database 16 | */ 17 | trait DecimalLength 18 | { 19 | protected function renderLength() : ?string 20 | { 21 | if ( ! isset($this->length[0])) { 22 | return null; 23 | } 24 | $maximum = $this->database->quote($this->length[0]); 25 | if (isset($this->length[1])) { 26 | $decimals = $this->database->quote($this->length[1]); 27 | $maximum .= ",{$decimals}"; 28 | } 29 | return "({$maximum})"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Definition/Table/DefinitionPart.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table; 11 | 12 | use BadMethodCallException; 13 | 14 | /** 15 | * Class DefinitionPart. 16 | * 17 | * @package database 18 | */ 19 | abstract class DefinitionPart 20 | { 21 | /** 22 | * @param string $method 23 | * @param array $arguments 24 | * 25 | * @return mixed 26 | */ 27 | public function __call(string $method, array $arguments) : mixed 28 | { 29 | if ($method === 'sql') { 30 | return $this->sql(...$arguments); 31 | } 32 | throw new BadMethodCallException("Method not found or not allowed: {$method}"); 33 | } 34 | 35 | abstract protected function sql() : string; 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Aplus Framework Database Library 2 | 3 | # Aplus Framework Database Library 4 | 5 | - [Home](https://aplus-framework.com/packages/database) 6 | - [User Guide](https://docs.aplus-framework.com/guides/libraries/database/index.html) 7 | - [API Documentation](https://docs.aplus-framework.com/packages/database.html) 8 | 9 | [![tests](https://github.com/aplus-framework/database/actions/workflows/tests.yml/badge.svg)](https://github.com/aplus-framework/database/actions/workflows/tests.yml) 10 | [![coverage](https://coveralls.io/repos/github/aplus-framework/database/badge.svg?branch=master)](https://coveralls.io/github/aplus-framework/database?branch=master) 11 | [![packagist](https://img.shields.io/packagist/v/aplus/database)](https://packagist.org/packages/aplus/database) 12 | [![open-source](https://img.shields.io/badge/open--source-sponsor-magenta)](https://aplus-framework.com/sponsor) 13 | -------------------------------------------------------------------------------- /src/Definition/Table/Constraint.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table; 11 | 12 | /** 13 | * Trait Constraint. 14 | * 15 | * @package database 16 | */ 17 | trait Constraint 18 | { 19 | protected ?string $constraint = null; 20 | 21 | /** 22 | * @param string $name 23 | * 24 | * @return static 25 | */ 26 | public function constraint(string $name) : static 27 | { 28 | $this->constraint = $name; 29 | return $this; 30 | } 31 | 32 | protected function renderConstraint() : ?string 33 | { 34 | if ($this->constraint === null) { 35 | return null; 36 | } 37 | $constraint = $this->database->protectIdentifier($this->constraint); 38 | return " CONSTRAINT {$constraint}"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Natan Felles 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Definition/Table/Check.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table; 11 | 12 | use Closure; 13 | use Framework\Database\Database; 14 | 15 | /** 16 | * Class Check. 17 | * 18 | * @see https://mariadb.com/kb/en/constraint/#check-constraints 19 | * 20 | * @package database 21 | */ 22 | class Check extends DefinitionPart 23 | { 24 | use Constraint; 25 | 26 | protected Database $database; 27 | protected Closure $check; 28 | 29 | public function __construct(Database $database, Closure $check) 30 | { 31 | $this->database = $database; 32 | $this->check = $check; 33 | } 34 | 35 | protected function renderCheck() : ?string 36 | { 37 | return ' CHECK (' . ($this->check)($this->database) . ')'; 38 | } 39 | 40 | protected function sql() : string 41 | { 42 | $sql = $this->renderConstraint(); 43 | $sql .= $this->renderCheck(); 44 | return $sql; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Manipulation/Traits/Select.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Manipulation\Traits; 11 | 12 | use Closure; 13 | use Framework\Database\Manipulation\Select as SelectStatement; 14 | use LogicException; 15 | 16 | /** 17 | * Trait Select. 18 | * 19 | * @package database 20 | */ 21 | trait Select 22 | { 23 | /** 24 | * Sets the SELECT statement part. 25 | * 26 | * @param Closure $select 27 | * 28 | * @see https://mariadb.com/kb/en/insert-select/ 29 | * 30 | * @return static 31 | */ 32 | public function select(Closure $select) : static 33 | { 34 | $this->sql['select'] = $select(new SelectStatement($this->database)); 35 | return $this; 36 | } 37 | 38 | protected function renderSelect() : ?string 39 | { 40 | if ( ! isset($this->sql['select'])) { 41 | return null; 42 | } 43 | if (isset($this->sql['values'])) { 44 | throw new LogicException('SELECT statement is not allowed when VALUES is set'); 45 | } 46 | if (isset($this->sql['set'])) { 47 | throw new LogicException('SELECT statement is not allowed when SET is set'); 48 | } 49 | return " {$this->sql['select']}"; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Statement.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database; 11 | 12 | /** 13 | * Class Statement. 14 | * 15 | * @package database 16 | */ 17 | abstract class Statement implements \Stringable 18 | { 19 | protected Database $database; 20 | /** 21 | * SQL clauses and parts. 22 | * 23 | * @var array 24 | */ 25 | protected array $sql = []; 26 | 27 | /** 28 | * Statement constructor. 29 | * 30 | * @param Database $database 31 | */ 32 | public function __construct(Database $database) 33 | { 34 | $this->database = $database; 35 | } 36 | 37 | public function __toString() : string 38 | { 39 | return $this->sql(); 40 | } 41 | 42 | /** 43 | * Resets SQL clauses and parts. 44 | * 45 | * @param string|null $sql A part name or null to reset all 46 | * 47 | * @see Statement::$sql 48 | * 49 | * @return static 50 | */ 51 | public function reset(string $sql = null) : static 52 | { 53 | if ($sql === null) { 54 | unset($this->sql); 55 | return $this; 56 | } 57 | unset($this->sql[$sql]); 58 | return $this; 59 | } 60 | 61 | /** 62 | * Renders the SQL statement. 63 | * 64 | * @return string 65 | */ 66 | abstract public function sql() : string; 67 | 68 | /** 69 | * Runs the SQL statement. 70 | * 71 | * @return mixed 72 | */ 73 | abstract public function run() : mixed; 74 | } 75 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/String/StringDataType.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\String; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class StringDataType. 16 | * 17 | * @package database 18 | */ 19 | abstract class StringDataType extends Column 20 | { 21 | protected string $charset; 22 | protected string $collation; 23 | 24 | /** 25 | * @param string $charset 26 | * 27 | * @return static 28 | */ 29 | public function charset(string $charset) : static 30 | { 31 | $this->charset = $charset; 32 | return $this; 33 | } 34 | 35 | protected function renderCharset() : ?string 36 | { 37 | if ( ! isset($this->charset)) { 38 | return null; 39 | } 40 | return ' CHARACTER SET ' . $this->database->quote($this->charset); 41 | } 42 | 43 | /** 44 | * @param string $collation 45 | * 46 | * @return static 47 | */ 48 | public function collate(string $collation) : static 49 | { 50 | $this->collation = $collation; 51 | return $this; 52 | } 53 | 54 | protected function renderCollate() : ?string 55 | { 56 | if ( ! isset($this->collation)) { 57 | return null; 58 | } 59 | return ' COLLATE ' . $this->database->quote($this->collation); 60 | } 61 | 62 | protected function renderTypeAttributes() : ?string 63 | { 64 | return $this->renderCharset() . $this->renderCollate(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Definition/DropSchema.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition; 11 | 12 | use Framework\Database\Statement; 13 | use LogicException; 14 | 15 | /** 16 | * Class DropSchema. 17 | * 18 | * @see https://mariadb.com/kb/en/drop-database/ 19 | * 20 | * @package database 21 | */ 22 | class DropSchema extends Statement 23 | { 24 | /** 25 | * @return static 26 | */ 27 | public function ifExists() : static 28 | { 29 | $this->sql['if_exists'] = true; 30 | return $this; 31 | } 32 | 33 | protected function renderIfExists() : ?string 34 | { 35 | if ( ! isset($this->sql['if_exists'])) { 36 | return null; 37 | } 38 | return ' IF EXISTS'; 39 | } 40 | 41 | /** 42 | * @param string $schemaName 43 | * 44 | * @return static 45 | */ 46 | public function schema(string $schemaName) : static 47 | { 48 | $this->sql['schema'] = $schemaName; 49 | return $this; 50 | } 51 | 52 | protected function renderSchema() : string 53 | { 54 | if (isset($this->sql['schema'])) { 55 | return ' ' . $this->database->protectIdentifier($this->sql['schema']); 56 | } 57 | throw new LogicException('SCHEMA name must be set'); 58 | } 59 | 60 | public function sql() : string 61 | { 62 | $sql = 'DROP SCHEMA' . $this->renderIfExists(); 63 | $sql .= $this->renderSchema() . \PHP_EOL; 64 | return $sql; 65 | } 66 | 67 | /** 68 | * Runs the CREATE SCHEMA statement. 69 | * 70 | * @return int|string The number of affected rows 71 | */ 72 | public function run() : int|string 73 | { 74 | return $this->database->exec($this->sql()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aplus/database", 3 | "description": "Aplus Framework Database Library", 4 | "license": "MIT", 5 | "type": "library", 6 | "keywords": [ 7 | "database", 8 | "mariadb", 9 | "mysql", 10 | "mysqli", 11 | "data-definition", 12 | "data-manipulation" 13 | ], 14 | "authors": [ 15 | { 16 | "name": "Natan Felles", 17 | "email": "natanfelles@gmail.com", 18 | "homepage": "https://natanfelles.github.io" 19 | } 20 | ], 21 | "homepage": "https://aplus-framework.com/packages/database", 22 | "support": { 23 | "email": "support@aplus-framework.com", 24 | "issues": "https://github.com/aplus-framework/database/issues", 25 | "forum": "https://aplus-framework.com/forum", 26 | "source": "https://github.com/aplus-framework/database", 27 | "docs": "https://docs.aplus-framework.com/guides/libraries/database/" 28 | }, 29 | "funding": [ 30 | { 31 | "type": "Aplus Sponsor", 32 | "url": "https://aplus-framework.com/sponsor" 33 | } 34 | ], 35 | "require": { 36 | "php": ">=8.1", 37 | "ext-mysqli": "*", 38 | "aplus/debug": "^3.0", 39 | "aplus/log": "^3.0" 40 | }, 41 | "require-dev": { 42 | "ext-xdebug": "*", 43 | "aplus/coding-standard": "^1.14", 44 | "ergebnis/composer-normalize": "^2.25", 45 | "jetbrains/phpstorm-attributes": "^1.0", 46 | "phpmd/phpmd": "^2.13", 47 | "phpstan/phpstan": "^1.9", 48 | "phpunit/phpunit": "^9.5" 49 | }, 50 | "minimum-stability": "dev", 51 | "prefer-stable": true, 52 | "autoload": { 53 | "psr-4": { 54 | "Framework\\Database\\": "src/" 55 | } 56 | }, 57 | "autoload-dev": { 58 | "psr-4": { 59 | "Tests\\Database\\": "tests/" 60 | } 61 | }, 62 | "config": { 63 | "allow-plugins": { 64 | "ergebnis/composer-normalize": true 65 | }, 66 | "optimize-autoloader": true, 67 | "preferred-install": "dist", 68 | "sort-packages": true 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Manipulation/Traits/Values.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Manipulation\Traits; 11 | 12 | use Closure; 13 | use LogicException; 14 | 15 | /** 16 | * Trait Values. 17 | * 18 | * @package database 19 | */ 20 | trait Values 21 | { 22 | /** 23 | * Adds a row of values to the VALUES clause. 24 | * 25 | * @param array>|Closure|float|int|string|null $value 26 | * @param Closure|float|int|string|null ...$values 27 | * 28 | * @return static 29 | */ 30 | public function values( 31 | array | Closure | float | int | string | null $value, 32 | Closure | float | int | string | null ...$values 33 | ) : static { 34 | if ( ! \is_array($value)) { 35 | $this->sql['values'][] = [$value, ...$values]; 36 | return $this; 37 | } 38 | if ($values) { 39 | throw new LogicException( 40 | 'The method ' . static::class . '::values' 41 | . ' must have only one argument when the first parameter is passed as array' 42 | ); 43 | } 44 | foreach ($value as $row) { 45 | $this->sql['values'][] = $row; 46 | } 47 | return $this; 48 | } 49 | 50 | /** 51 | * Renders the VALUES clause. 52 | * 53 | * @return string|null The VALUES part or null if none was set 54 | */ 55 | protected function renderValues() : ?string 56 | { 57 | if ( ! isset($this->sql['values'])) { 58 | return null; 59 | } 60 | $values = []; 61 | foreach ($this->sql['values'] as $value) { 62 | foreach ($value as &$item) { 63 | $item = $this->renderValue($item); 64 | } 65 | unset($item); 66 | $values[] = ' (' . \implode(', ', $value) . ')'; 67 | } 68 | $values = \implode(',' . \PHP_EOL, $values); 69 | return " VALUES{$values}"; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Definition/Table/Indexes/Index.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Indexes; 11 | 12 | use Framework\Database\Database; 13 | use Framework\Database\Definition\Table\DefinitionPart; 14 | use LogicException; 15 | 16 | /** 17 | * Class Index. 18 | * 19 | * @see https://mariadb.com/kb/en/getting-started-with-indexes/ 20 | * 21 | * @package database 22 | */ 23 | abstract class Index extends DefinitionPart 24 | { 25 | protected Database $database; 26 | /** 27 | * @var array 28 | */ 29 | protected array $columns; 30 | protected string $type = ''; 31 | protected ?string $name; 32 | 33 | public function __construct(Database $database, ?string $name, string $column, string ...$columns) 34 | { 35 | $this->database = $database; 36 | $this->name = $name; 37 | $this->columns = $columns ? \array_merge([$column], $columns) : [$column]; 38 | } 39 | 40 | protected function renderType() : string 41 | { 42 | if (empty($this->type)) { 43 | throw new LogicException('Key type is empty'); 44 | } 45 | return " {$this->type}"; 46 | } 47 | 48 | protected function renderName() : ?string 49 | { 50 | if ($this->name === null) { 51 | return null; 52 | } 53 | return ' ' . $this->database->protectIdentifier($this->name); 54 | } 55 | 56 | protected function renderColumns() : string 57 | { 58 | $columns = []; 59 | foreach ($this->columns as $column) { 60 | $columns[] = $this->database->protectIdentifier($column); 61 | } 62 | $columns = \implode(', ', $columns); 63 | return " ({$columns})"; 64 | } 65 | 66 | protected function renderTypeAttributes() : ?string 67 | { 68 | return null; 69 | } 70 | 71 | protected function sql() : string 72 | { 73 | $sql = $this->renderType(); 74 | $sql .= $this->renderName(); 75 | $sql .= $this->renderColumns(); 76 | $sql .= $this->renderTypeAttributes(); 77 | return $sql; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Manipulation/Traits/Set.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Manipulation\Traits; 11 | 12 | use Closure; 13 | use LogicException; 14 | 15 | /** 16 | * Trait Set. 17 | * 18 | * @package database 19 | */ 20 | trait Set 21 | { 22 | /** 23 | * Sets the SET clause. 24 | * 25 | * @param array|object $columns Array 26 | * of columns => values or an object to be cast to array 27 | * 28 | * @return static 29 | */ 30 | public function set(array | object $columns) : static 31 | { 32 | $this->sql['set'] = (array) $columns; 33 | return $this; 34 | } 35 | 36 | /** 37 | * Renders the SET clause. 38 | * 39 | * @return string|null The SET clause null if it was not set 40 | */ 41 | protected function renderSet() : ?string 42 | { 43 | if ( ! $this->hasSet()) { 44 | return null; 45 | } 46 | $set = []; 47 | foreach ($this->sql['set'] as $column => $value) { 48 | $set[] = $this->renderAssignment($column, $value); 49 | } 50 | $set = \implode(', ', $set); 51 | return " SET {$set}"; 52 | } 53 | 54 | /** 55 | * Renders the SET clause checking conflicts. 56 | * 57 | * @throws LogicException if SET was set with columns or with the VALUES clause 58 | * 59 | * @return string|null The SET part or null if it was not set 60 | */ 61 | protected function renderSetCheckingConflicts() : ?string 62 | { 63 | $part = $this->renderSet(); 64 | if ($part === null) { 65 | return null; 66 | } 67 | if (isset($this->sql['columns'])) { 68 | throw new LogicException('SET clause is not allowed when columns are set'); 69 | } 70 | if (isset($this->sql['values'])) { 71 | throw new LogicException('SET clause is not allowed when VALUES is set'); 72 | } 73 | return $part; 74 | } 75 | 76 | /** 77 | * Tells if the SET clause was set. 78 | * 79 | * @return bool True if was set, otherwise false 80 | */ 81 | protected function hasSet() : bool 82 | { 83 | return isset($this->sql['set']); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Numeric/NumericDataType.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns\Numeric; 11 | 12 | use Framework\Database\Definition\Table\Columns\Column; 13 | 14 | /** 15 | * Class NumericDataType. 16 | * 17 | * @package database 18 | */ 19 | abstract class NumericDataType extends Column 20 | { 21 | protected bool $signed = false; 22 | protected bool $unsigned = false; 23 | protected bool $zerofill = false; 24 | protected bool $autoIncrement = false; 25 | 26 | /** 27 | * @see https://mariadb.com/kb/en/auto_increment/ 28 | * 29 | * @return static 30 | */ 31 | public function autoIncrement() : static 32 | { 33 | $this->autoIncrement = true; 34 | return $this; 35 | } 36 | 37 | protected function renderAutoIncrement() : ?string 38 | { 39 | if ( ! $this->autoIncrement) { 40 | return null; 41 | } 42 | return ' AUTO_INCREMENT'; 43 | } 44 | 45 | /** 46 | * @return static 47 | */ 48 | public function signed() : static 49 | { 50 | $this->signed = true; 51 | return $this; 52 | } 53 | 54 | protected function renderSigned() : ?string 55 | { 56 | if ( ! $this->signed) { 57 | return null; 58 | } 59 | return ' signed'; 60 | } 61 | 62 | /** 63 | * @return static 64 | */ 65 | public function unsigned() : static 66 | { 67 | $this->unsigned = true; 68 | return $this; 69 | } 70 | 71 | protected function renderUnsigned() : ?string 72 | { 73 | if ( ! $this->unsigned) { 74 | return null; 75 | } 76 | return ' unsigned'; 77 | } 78 | 79 | /** 80 | * @return static 81 | */ 82 | public function zerofill() : static 83 | { 84 | $this->zerofill = true; 85 | return $this; 86 | } 87 | 88 | protected function renderZerofill() : ?string 89 | { 90 | if ( ! $this->zerofill) { 91 | return null; 92 | } 93 | return ' zerofill'; 94 | } 95 | 96 | protected function renderTypeAttributes() : ?string 97 | { 98 | return $this->renderSigned() 99 | . $this->renderUnsigned() 100 | . $this->renderZerofill() 101 | . $this->renderAutoIncrement(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Definition/Table/Indexes/IndexDefinition.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Indexes; 11 | 12 | use Framework\Database\Database; 13 | use Framework\Database\Definition\Table\DefinitionPart; 14 | use Framework\Database\Definition\Table\Indexes\Keys\ForeignKey; 15 | use Framework\Database\Definition\Table\Indexes\Keys\FulltextKey; 16 | use Framework\Database\Definition\Table\Indexes\Keys\Key; 17 | use Framework\Database\Definition\Table\Indexes\Keys\PrimaryKey; 18 | use Framework\Database\Definition\Table\Indexes\Keys\SpatialKey; 19 | use Framework\Database\Definition\Table\Indexes\Keys\UniqueKey; 20 | use RuntimeException; 21 | 22 | /** 23 | * Class IndexDefinition. 24 | * 25 | * @see https://mariadb.com/kb/en/create-table/#index-definitions 26 | * @see https://mariadb.com/kb/en/optimization-and-indexes/ 27 | * 28 | * @package database 29 | */ 30 | class IndexDefinition extends DefinitionPart 31 | { 32 | protected Database $database; 33 | protected ?string $name; 34 | protected ?Index $index = null; 35 | 36 | public function __construct(Database $database, string $name = null) 37 | { 38 | $this->database = $database; 39 | $this->name = $name; 40 | } 41 | 42 | public function key(string $column, string ...$columns) : Key 43 | { 44 | return $this->index = new Key($this->database, $this->name, $column, ...$columns); 45 | } 46 | 47 | public function primaryKey(string $column, string ...$columns) : PrimaryKey 48 | { 49 | return $this->index = new PrimaryKey($this->database, $this->name, $column, ...$columns); 50 | } 51 | 52 | public function uniqueKey(string $column, string ...$columns) : UniqueKey 53 | { 54 | return $this->index = new UniqueKey($this->database, $this->name, $column, ...$columns); 55 | } 56 | 57 | public function fulltextKey(string $column, string ...$columns) : FulltextKey 58 | { 59 | return $this->index = new FulltextKey($this->database, $this->name, $column, ...$columns); 60 | } 61 | 62 | public function foreignKey(string $column, string ...$columns) : ForeignKey 63 | { 64 | return $this->index = new ForeignKey($this->database, $this->name, $column, ...$columns); 65 | } 66 | 67 | public function spatialKey(string $column, string ...$columns) : SpatialKey 68 | { 69 | return $this->index = new SpatialKey($this->database, $this->name, $column, ...$columns); 70 | } 71 | 72 | protected function sql() : string 73 | { 74 | if ( ! $this->index) { 75 | throw new RuntimeException("Key type not set in index {$this->name}"); 76 | } 77 | return $this->index->sql(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Manipulation/Traits/OrderBy.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Manipulation\Traits; 11 | 12 | use Closure; 13 | 14 | /** 15 | * Trait OrderBy. 16 | * 17 | * @see https://mariadb.com/kb/en/order-by/ 18 | * 19 | * @package database 20 | */ 21 | trait OrderBy 22 | { 23 | /** 24 | * Appends columns to the ORDER BY clause. 25 | * 26 | * @param Closure|string $column The column name or a subquery 27 | * @param Closure|string ...$columns Extra column names and/or subqueries 28 | * 29 | * @return static 30 | */ 31 | public function orderBy(Closure | string $column, Closure | string ...$columns) : static 32 | { 33 | return $this->addOrderBy($column, $columns, null); 34 | } 35 | 36 | /** 37 | * Appends columns with the ASC direction to the ORDER BY clause. 38 | * 39 | * @param Closure|string $column The column name or a subquery 40 | * @param Closure|string ...$columns Extra column names and/or subqueries 41 | * 42 | * @return static 43 | */ 44 | public function orderByAsc(Closure | string $column, Closure | string ...$columns) : static 45 | { 46 | return $this->addOrderBy($column, $columns, 'ASC'); 47 | } 48 | 49 | /** 50 | * Appends columns with the DESC direction to the ORDER BY clause. 51 | * 52 | * @param Closure|string $column The column name or a subquery 53 | * @param Closure|string ...$columns Extra column names and/or subqueries 54 | * 55 | * @return static 56 | */ 57 | public function orderByDesc(Closure | string $column, Closure | string ...$columns) : static 58 | { 59 | return $this->addOrderBy($column, $columns, 'DESC'); 60 | } 61 | 62 | /** 63 | * Adds a ORDER BY expression. 64 | * 65 | * @param Closure|string $column The column name or a subquery 66 | * @param array $columns Extra column names and/or subqueries 67 | * @param string|null $direction `ASC`, `DESC` or null for none 68 | * 69 | * @return static 70 | */ 71 | private function addOrderBy(Closure | string $column, array $columns, ?string $direction) : static 72 | { 73 | foreach ([$column, ...$columns] as $column) { 74 | $this->sql['order_by'][] = [ 75 | 'column' => $column, 76 | 'direction' => $direction, 77 | ]; 78 | } 79 | return $this; 80 | } 81 | 82 | /** 83 | * Renders the ORDER BY clause. 84 | * 85 | * @return string|null The ORDER BY clause or null if it was not set 86 | */ 87 | protected function renderOrderBy() : ?string 88 | { 89 | if ( ! isset($this->sql['order_by'])) { 90 | return null; 91 | } 92 | $expressions = []; 93 | foreach ($this->sql['order_by'] as $part) { 94 | $expression = $this->renderIdentifier($part['column']); 95 | if ($part['direction']) { 96 | $expression .= " {$part['direction']}"; 97 | } 98 | $expressions[] = $expression; 99 | } 100 | $expressions = \implode(', ', $expressions); 101 | return " ORDER BY {$expressions}"; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Manipulation/Traits/GroupBy.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Manipulation\Traits; 11 | 12 | use Closure; 13 | 14 | /** 15 | * Trait GroupBy. 16 | * 17 | * @see https://mariadb.com/kb/en/group-by/ 18 | * 19 | * @package database 20 | * 21 | * @since 3.4 22 | */ 23 | trait GroupBy 24 | { 25 | /** 26 | * Appends columns to the GROUP BY clause. 27 | * 28 | * @param Closure|string $column The column name or a subquery 29 | * @param Closure|string ...$columns Extra column names and/or subqueries 30 | * 31 | * @return static 32 | */ 33 | public function groupBy(Closure | string $column, Closure | string ...$columns) : static 34 | { 35 | return $this->addGroupBy($column, $columns, null); 36 | } 37 | 38 | /** 39 | * Appends columns with the ASC direction to the GROUP BY clause. 40 | * 41 | * @param Closure|string $column The column name or a subquery 42 | * @param Closure|string ...$columns Extra column names and/or subqueries 43 | * 44 | * @return static 45 | */ 46 | public function groupByAsc(Closure | string $column, Closure | string ...$columns) : static 47 | { 48 | return $this->addGroupBy($column, $columns, 'ASC'); 49 | } 50 | 51 | /** 52 | * Appends columns with the DESC direction to the GROUP BY clause. 53 | * 54 | * @param Closure|string $column The column name or a subquery 55 | * @param Closure|string ...$columns Extra column names and/or subqueries 56 | * 57 | * @return static 58 | */ 59 | public function groupByDesc(Closure | string $column, Closure | string ...$columns) : static 60 | { 61 | return $this->addGroupBy($column, $columns, 'DESC'); 62 | } 63 | 64 | /** 65 | * Adds a GROUP BY expression. 66 | * 67 | * @param Closure|string $column The column name or a subquery 68 | * @param array $columns Extra column names and/or subqueries 69 | * @param string|null $direction `ASC`, `DESC` or null for none 70 | * 71 | * @return static 72 | */ 73 | private function addGroupBy(Closure | string $column, array $columns, ?string $direction) : static 74 | { 75 | foreach ([$column, ...$columns] as $column) { 76 | $this->sql['group_by'][] = [ 77 | 'column' => $column, 78 | 'direction' => $direction, 79 | ]; 80 | } 81 | return $this; 82 | } 83 | 84 | /** 85 | * Renders the GROUP BY clause. 86 | * 87 | * @return string|null The GROUP BY clause or null if it was not set 88 | */ 89 | protected function renderGroupBy() : ?string 90 | { 91 | if ( ! isset($this->sql['group_by'])) { 92 | return null; 93 | } 94 | $expressions = []; 95 | foreach ($this->sql['group_by'] as $part) { 96 | $expression = $this->renderIdentifier($part['column']); 97 | if ($part['direction']) { 98 | $expression .= " {$part['direction']}"; 99 | } 100 | $expressions[] = $expression; 101 | } 102 | $expressions = \implode(', ', $expressions); 103 | return " GROUP BY {$expressions}"; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/PreparedStatement.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database; 11 | 12 | use InvalidArgumentException; 13 | use RuntimeException; 14 | 15 | /** 16 | * Class PreparedStatement. 17 | * 18 | * @package database 19 | */ 20 | class PreparedStatement 21 | { 22 | protected \mysqli_stmt $statement; 23 | protected bool $sendingBlob = false; 24 | 25 | public function __construct(\mysqli_stmt $statement) 26 | { 27 | $this->statement = $statement; 28 | } 29 | 30 | /** 31 | * Executes the prepared statement, returning a result set as a Result object. 32 | * 33 | * @param bool|float|int|string|null ...$params Parameters sent to the prepared statement 34 | * 35 | * @throws RuntimeException if it cannot obtain a result set from the prepared statement 36 | * 37 | * @return Result 38 | */ 39 | public function query(bool | float | int | string | null ...$params) : Result 40 | { 41 | $this->bindParams($params); 42 | $this->statement->execute(); 43 | $result = $this->statement->get_result(); 44 | if ($result === false) { 45 | throw new RuntimeException('Failed while trying to obtain a result set from the prepared statement'); 46 | } 47 | return new Result($result, true); 48 | } 49 | 50 | /** 51 | * Executes the prepared statement and return the number of affected rows. 52 | * 53 | * @param bool|float|int|string|null ...$params Parameters sent to the prepared statement 54 | * 55 | * @return int|string 56 | */ 57 | public function exec(bool | float | int | string | null ...$params) : int|string 58 | { 59 | $this->bindParams($params); 60 | $this->statement->execute(); 61 | if ($this->statement->field_count) { 62 | $this->statement->free_result(); 63 | } 64 | return $this->statement->affected_rows; 65 | } 66 | 67 | /** 68 | * @param array|mixed[] $params Values types: bool, float, int, string or null 69 | */ 70 | protected function bindParams(array $params) : void 71 | { 72 | $this->sendingBlob = false; 73 | if (empty($params)) { 74 | return; 75 | } 76 | $types = ''; 77 | foreach ($params as &$param) { 78 | $type = \gettype($param); 79 | switch ($type) { 80 | case 'boolean': 81 | $types .= 'i'; 82 | $param = (int) $param; 83 | break; 84 | case 'double': 85 | $types .= 'd'; 86 | break; 87 | case 'integer': 88 | $types .= 'i'; 89 | break; 90 | case 'NULL': 91 | case 'string': 92 | $types .= 's'; 93 | break; 94 | default: 95 | throw new InvalidArgumentException( 96 | "Invalid param data type: {$type}" 97 | ); 98 | } 99 | } 100 | unset($param); 101 | $this->statement->bind_param($types, ...$params); 102 | } 103 | 104 | public function sendBlob(string $chunk) : bool 105 | { 106 | if ( ! $this->sendingBlob) { 107 | $this->sendingBlob = true; 108 | $null = null; 109 | $this->statement->bind_param('b', $null); 110 | } 111 | return $this->statement->send_long_data(0, $chunk); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Manipulation/With.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Manipulation; 11 | 12 | use Closure; 13 | use Framework\Database\Result; 14 | use InvalidArgumentException; 15 | use LogicException; 16 | 17 | /** 18 | * Class With. 19 | * 20 | * @see https://mariadb.com/kb/en/with/ 21 | * 22 | * @package database 23 | */ 24 | class With extends Statement 25 | { 26 | /** 27 | * @see https://mariadb.com/kb/en/recursive-common-table-expressions-overview/ 28 | * 29 | * @var string 30 | */ 31 | public const OPT_RECURSIVE = 'RECURSIVE'; 32 | 33 | protected function renderOptions() : ?string 34 | { 35 | if ( ! $this->hasOptions()) { 36 | return null; 37 | } 38 | $options = $this->sql['options']; 39 | foreach ($options as &$option) { 40 | $input = $option; 41 | $option = \strtoupper($option); 42 | if ($option !== static::OPT_RECURSIVE) { 43 | throw new InvalidArgumentException("Invalid option: {$input}"); 44 | } 45 | } 46 | return \implode(' ', $options); 47 | } 48 | 49 | /** 50 | * Adds a table reference. 51 | * 52 | * @param Closure|string $table 53 | * @param Closure $alias 54 | * 55 | * @see https://mariadb.com/kb/en/non-recursive-common-table-expressions-overview/ 56 | * @see https://mariadb.com/kb/en/recursive-common-table-expressions-overview/ 57 | * 58 | * @return static 59 | */ 60 | public function reference(Closure | string $table, Closure $alias) : static 61 | { 62 | $this->sql['references'][] = [ 63 | 'table' => $table, 64 | 'alias' => $alias, 65 | ]; 66 | return $this; 67 | } 68 | 69 | protected function renderReference() : string 70 | { 71 | if ( ! isset($this->sql['references'])) { 72 | throw new LogicException('References must be set'); 73 | } 74 | $references = []; 75 | foreach ($this->sql['references'] as $reference) { 76 | $references[] = $this->renderIdentifier($reference['table']) 77 | . ' AS ' . $this->renderAsSelect($reference['alias']); 78 | } 79 | return \implode(', ', $references); 80 | } 81 | 82 | private function renderAsSelect(Closure $subquery) : string 83 | { 84 | return '(' . $subquery(new Select($this->database)) . ')'; 85 | } 86 | 87 | /** 88 | * Sets the SELECT statement part. 89 | * 90 | * @param Closure $select 91 | * 92 | * @return static 93 | */ 94 | public function select(Closure $select) : static 95 | { 96 | $this->sql['select'] = $select(new Select($this->database)); 97 | return $this; 98 | } 99 | 100 | protected function renderSelect() : string 101 | { 102 | if ( ! isset($this->sql['select'])) { 103 | throw new LogicException('SELECT must be set'); 104 | } 105 | return $this->sql['select']; 106 | } 107 | 108 | /** 109 | * Renders the WITH statement. 110 | * 111 | * @return string 112 | */ 113 | public function sql() : string 114 | { 115 | $sql = 'WITH' . \PHP_EOL; 116 | $part = $this->renderOptions(); 117 | if ($part) { 118 | $sql .= $part . \PHP_EOL; 119 | } 120 | $sql .= $this->renderReference() . \PHP_EOL; 121 | $sql .= $this->renderSelect(); 122 | return $sql; 123 | } 124 | 125 | /** 126 | * Runs the WITH statement. 127 | * 128 | * @return Result 129 | */ 130 | public function run() : Result 131 | { 132 | return $this->database->query($this->sql()); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/Definition/CreateSchema.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition; 11 | 12 | use Framework\Database\Statement; 13 | use LogicException; 14 | 15 | /** 16 | * Class CreateSchema. 17 | * 18 | * @see https://mariadb.com/kb/en/create-database/ 19 | * 20 | * @package database 21 | */ 22 | class CreateSchema extends Statement 23 | { 24 | /** 25 | * @return static 26 | */ 27 | public function orReplace() : static 28 | { 29 | $this->sql['or_replace'] = true; 30 | return $this; 31 | } 32 | 33 | protected function renderOrReplace() : ?string 34 | { 35 | if ( ! isset($this->sql['or_replace'])) { 36 | return null; 37 | } 38 | return ' OR REPLACE'; 39 | } 40 | 41 | /** 42 | * @return static 43 | */ 44 | public function ifNotExists() : static 45 | { 46 | $this->sql['if_not_exists'] = true; 47 | return $this; 48 | } 49 | 50 | protected function renderIfNotExists() : ?string 51 | { 52 | if ( ! isset($this->sql['if_not_exists'])) { 53 | return null; 54 | } 55 | if (isset($this->sql['or_replace'])) { 56 | throw new LogicException( 57 | 'Clauses OR REPLACE and IF NOT EXISTS can not be used together' 58 | ); 59 | } 60 | return ' IF NOT EXISTS'; 61 | } 62 | 63 | /** 64 | * @param string $schemaName 65 | * 66 | * @return static 67 | */ 68 | public function schema(string $schemaName) : static 69 | { 70 | $this->sql['schema'] = $schemaName; 71 | return $this; 72 | } 73 | 74 | protected function renderSchema() : string 75 | { 76 | if (isset($this->sql['schema'])) { 77 | return ' ' . $this->database->protectIdentifier($this->sql['schema']); 78 | } 79 | throw new LogicException('SCHEMA name must be set'); 80 | } 81 | 82 | /** 83 | * @param string $charset 84 | * 85 | * @return static 86 | */ 87 | public function charset(string $charset) : static 88 | { 89 | $this->sql['charset'] = $charset; 90 | return $this; 91 | } 92 | 93 | protected function renderCharset() : ?string 94 | { 95 | if ( ! isset($this->sql['charset'])) { 96 | return null; 97 | } 98 | $charset = $this->database->quote($this->sql['charset']); 99 | return " CHARACTER SET = {$charset}"; 100 | } 101 | 102 | /** 103 | * @param string $collation 104 | * 105 | * @return static 106 | */ 107 | public function collate(string $collation) : static 108 | { 109 | $this->sql['collation'] = $collation; 110 | return $this; 111 | } 112 | 113 | protected function renderCollate() : ?string 114 | { 115 | if ( ! isset($this->sql['collation'])) { 116 | return null; 117 | } 118 | $collation = $this->database->quote($this->sql['collation']); 119 | return " COLLATE = {$collation}"; 120 | } 121 | 122 | public function sql() : string 123 | { 124 | $sql = 'CREATE' . $this->renderOrReplace(); 125 | $sql .= ' SCHEMA' . $this->renderIfNotExists(); 126 | $sql .= $this->renderSchema() . \PHP_EOL; 127 | $part = $this->renderCharset(); 128 | if ($part) { 129 | $sql .= $part . \PHP_EOL; 130 | } 131 | $part = $this->renderCollate(); 132 | if ($part) { 133 | $sql .= $part . \PHP_EOL; 134 | } 135 | return $sql; 136 | } 137 | 138 | /** 139 | * Runs the CREATE SCHEMA statement. 140 | * 141 | * @return int|string The number of affected rows 142 | */ 143 | public function run() : int|string 144 | { 145 | return $this->database->exec($this->sql()); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/Definition/CreateTable.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition; 11 | 12 | use Framework\Database\Definition\Table\TableDefinition; 13 | use Framework\Database\Definition\Table\TableStatement; 14 | use LogicException; 15 | 16 | /** 17 | * Class CreateTable. 18 | * 19 | * @see https://mariadb.com/kb/en/create-table/ 20 | * 21 | * @package database 22 | */ 23 | class CreateTable extends TableStatement 24 | { 25 | /** 26 | * Adds a OR REPLACE part. 27 | * 28 | * WARNING: This feature is MariaDB only. It is not compatible with MySQL. 29 | * 30 | * @return static 31 | */ 32 | public function orReplace() : static 33 | { 34 | $this->sql['or_replace'] = true; 35 | return $this; 36 | } 37 | 38 | protected function renderOrReplace() : ?string 39 | { 40 | if ( ! isset($this->sql['or_replace'])) { 41 | return null; 42 | } 43 | return ' OR REPLACE'; 44 | } 45 | 46 | /** 47 | * @return static 48 | */ 49 | public function temporary() : static 50 | { 51 | $this->sql['temporary'] = true; 52 | return $this; 53 | } 54 | 55 | protected function renderTemporary() : ?string 56 | { 57 | if ( ! isset($this->sql['temporary'])) { 58 | return null; 59 | } 60 | return ' TEMPORARY'; 61 | } 62 | 63 | /** 64 | * @return static 65 | */ 66 | public function ifNotExists() : static 67 | { 68 | $this->sql['if_not_exists'] = true; 69 | return $this; 70 | } 71 | 72 | protected function renderIfNotExists() : ?string 73 | { 74 | if ( ! isset($this->sql['if_not_exists'])) { 75 | return null; 76 | } 77 | if (isset($this->sql['or_replace'])) { 78 | throw new LogicException( 79 | 'Clauses OR REPLACE and IF NOT EXISTS can not be used together' 80 | ); 81 | } 82 | return ' IF NOT EXISTS'; 83 | } 84 | 85 | /** 86 | * @param string $tableName 87 | * 88 | * @return static 89 | */ 90 | public function table(string $tableName) : static 91 | { 92 | $this->sql['table'] = $tableName; 93 | return $this; 94 | } 95 | 96 | protected function renderTable() : string 97 | { 98 | if (isset($this->sql['table'])) { 99 | return ' ' . $this->database->protectIdentifier($this->sql['table']); 100 | } 101 | throw new LogicException('TABLE name must be set'); 102 | } 103 | 104 | /** 105 | * @param callable $definition 106 | * 107 | * @return static 108 | */ 109 | public function definition(callable $definition) : static 110 | { 111 | $this->sql['definition'] = $definition; 112 | return $this; 113 | } 114 | 115 | protected function renderDefinition() : string 116 | { 117 | if ( ! isset($this->sql['definition'])) { 118 | throw new LogicException('Table definition must be set'); 119 | } 120 | $definition = new TableDefinition($this->database); 121 | $this->sql['definition']($definition); 122 | return $definition->sql(); 123 | } 124 | 125 | public function sql() : string 126 | { 127 | $sql = 'CREATE' . $this->renderOrReplace() . $this->renderTemporary(); 128 | $sql .= ' TABLE' . $this->renderIfNotExists(); 129 | $sql .= $this->renderTable() . ' (' . \PHP_EOL; 130 | $sql .= $this->renderDefinition() . \PHP_EOL; 131 | $sql .= ')' . $this->renderOptions(); 132 | return $sql; 133 | } 134 | 135 | /** 136 | * Runs the CREATE TABLE statement. 137 | * 138 | * @return int|string The number of affected rows 139 | */ 140 | public function run() : int|string 141 | { 142 | return $this->database->exec($this->sql()); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/Definition/DropTable.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition; 11 | 12 | use Framework\Database\Statement; 13 | use InvalidArgumentException; 14 | use LogicException; 15 | 16 | /** 17 | * Class DropTable. 18 | * 19 | * @see https://mariadb.com/kb/en/drop-table/ 20 | * 21 | * @package database 22 | */ 23 | class DropTable extends Statement 24 | { 25 | /** 26 | * @return static 27 | */ 28 | public function temporary() : static 29 | { 30 | $this->sql['temporary'] = true; 31 | return $this; 32 | } 33 | 34 | protected function renderTemporary() : ?string 35 | { 36 | if ( ! isset($this->sql['temporary'])) { 37 | return null; 38 | } 39 | return ' TEMPORARY'; 40 | } 41 | 42 | /** 43 | * @return static 44 | */ 45 | public function ifExists() : static 46 | { 47 | $this->sql['if_exists'] = true; 48 | return $this; 49 | } 50 | 51 | protected function renderIfExists() : ?string 52 | { 53 | if ( ! isset($this->sql['if_exists'])) { 54 | return null; 55 | } 56 | return ' IF EXISTS'; 57 | } 58 | 59 | /** 60 | * @param string $comment 61 | * 62 | * @return static 63 | */ 64 | public function commentToSave(string $comment) : static 65 | { 66 | $this->sql['comment'] = $comment; 67 | return $this; 68 | } 69 | 70 | protected function renderCommentToSave() : ?string 71 | { 72 | if ( ! isset($this->sql['comment'])) { 73 | return null; 74 | } 75 | $comment = \strtr($this->sql['comment'], ['*/' => '* /']); 76 | return " /* {$comment} */"; 77 | } 78 | 79 | /** 80 | * @param string $table 81 | * @param string ...$tables 82 | * 83 | * @return static 84 | */ 85 | public function table(string $table, string ...$tables) : static 86 | { 87 | $this->sql['tables'] = $tables ? \array_merge([$table], $tables) : [$table]; 88 | return $this; 89 | } 90 | 91 | protected function renderTables() : string 92 | { 93 | if ( ! isset($this->sql['tables'])) { 94 | throw new LogicException('Table names can not be empty'); 95 | } 96 | $tables = $this->sql['tables']; 97 | foreach ($tables as &$table) { 98 | $table = $this->database->protectIdentifier($table); 99 | } 100 | unset($table); 101 | $tables = \implode(', ', $tables); 102 | return " {$tables}"; 103 | } 104 | 105 | /** 106 | * @param int $seconds 107 | * 108 | * @return static 109 | */ 110 | public function wait(int $seconds) : static 111 | { 112 | $this->sql['wait'] = $seconds; 113 | return $this; 114 | } 115 | 116 | public function renderWait() : ?string 117 | { 118 | if ( ! isset($this->sql['wait'])) { 119 | return null; 120 | } 121 | if ($this->sql['wait'] < 0) { 122 | throw new InvalidArgumentException( 123 | "Invalid WAIT value: {$this->sql['wait']}" 124 | ); 125 | } 126 | return " WAIT {$this->sql['wait']}"; 127 | } 128 | 129 | public function sql() : string 130 | { 131 | $sql = 'DROP' . $this->renderTemporary(); 132 | $sql .= ' TABLE' . $this->renderIfExists(); 133 | $sql .= $this->renderCommentToSave(); 134 | $sql .= $this->renderTables() . \PHP_EOL; 135 | $part = $this->renderWait(); 136 | if ($part) { 137 | $sql .= $part . \PHP_EOL; 138 | } 139 | return $sql; 140 | } 141 | 142 | /** 143 | * Runs the DROP TABLE statement. 144 | * 145 | * @return int|string The number of affected rows 146 | */ 147 | public function run() : int|string 148 | { 149 | return $this->database->exec($this->sql()); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/Definition/AlterSchema.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition; 11 | 12 | use Framework\Database\Statement; 13 | use LogicException; 14 | 15 | /** 16 | * Class AlterSchema. 17 | * 18 | * @see https://mariadb.com/kb/en/alter-database/ 19 | * 20 | * @package database 21 | */ 22 | class AlterSchema extends Statement 23 | { 24 | /** 25 | * @param string $schemaName 26 | * 27 | * @return static 28 | */ 29 | public function schema(string $schemaName) : static 30 | { 31 | $this->sql['schema'] = $schemaName; 32 | return $this; 33 | } 34 | 35 | protected function renderSchema() : ?string 36 | { 37 | if ( ! isset($this->sql['schema'])) { 38 | return null; 39 | } 40 | $schema = $this->sql['schema']; 41 | if (isset($this->sql['upgrade'])) { 42 | $schema = "#mysql50#{$schema}"; 43 | } 44 | return ' ' . $this->database->protectIdentifier($schema); 45 | } 46 | 47 | /** 48 | * @param string $charset 49 | * 50 | * @return static 51 | */ 52 | public function charset(string $charset) : static 53 | { 54 | $this->sql['charset'] = $charset; 55 | return $this; 56 | } 57 | 58 | protected function renderCharset() : ?string 59 | { 60 | if ( ! isset($this->sql['charset'])) { 61 | return null; 62 | } 63 | $charset = $this->database->quote($this->sql['charset']); 64 | return " CHARACTER SET = {$charset}"; 65 | } 66 | 67 | /** 68 | * @param string $collation 69 | * 70 | * @return static 71 | */ 72 | public function collate(string $collation) : static 73 | { 74 | $this->sql['collation'] = $collation; 75 | return $this; 76 | } 77 | 78 | protected function renderCollate() : ?string 79 | { 80 | if ( ! isset($this->sql['collation'])) { 81 | return null; 82 | } 83 | $collation = $this->database->quote($this->sql['collation']); 84 | return " COLLATE = {$collation}"; 85 | } 86 | 87 | /** 88 | * @return static 89 | */ 90 | public function upgrade() : static 91 | { 92 | $this->sql['upgrade'] = true; 93 | return $this; 94 | } 95 | 96 | protected function renderUpgrade() : ?string 97 | { 98 | if ( ! isset($this->sql['upgrade'])) { 99 | return null; 100 | } 101 | if (isset($this->sql['charset']) || isset($this->sql['collation'])) { 102 | throw new LogicException( 103 | 'UPGRADE DATA DIRECTORY NAME can not be used with CHARACTER SET or COLLATE' 104 | ); 105 | } 106 | return ' UPGRADE DATA DIRECTORY NAME'; 107 | } 108 | 109 | protected function checkSpecifications() : void 110 | { 111 | if ( ! isset($this->sql['charset']) 112 | && ! isset($this->sql['collation']) 113 | && ! isset($this->sql['upgrade']) 114 | ) { 115 | throw new LogicException( 116 | 'ALTER SCHEMA must have a specification' 117 | ); 118 | } 119 | } 120 | 121 | public function sql() : string 122 | { 123 | $sql = 'ALTER SCHEMA'; 124 | $sql .= $this->renderSchema() . \PHP_EOL; 125 | $part = $this->renderCharset(); 126 | if ($part) { 127 | $sql .= $part . \PHP_EOL; 128 | } 129 | $part = $this->renderCollate(); 130 | if ($part) { 131 | $sql .= $part . \PHP_EOL; 132 | } 133 | $part = $this->renderUpgrade(); 134 | if ($part) { 135 | $sql .= $part . \PHP_EOL; 136 | } 137 | $this->checkSpecifications(); 138 | return $sql; 139 | } 140 | 141 | /** 142 | * Runs the ALTER SCHEMA statement. 143 | * 144 | * @return int|string The number of affected rows 145 | */ 146 | public function run() : int|string 147 | { 148 | return $this->database->exec($this->sql()); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/Definition/Table/Indexes/Keys/ForeignKey.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Indexes\Keys; 11 | 12 | use InvalidArgumentException; 13 | use LogicException; 14 | 15 | /** 16 | * Class ForeignKey. 17 | * 18 | * @see https://mariadb.com/kb/en/foreign-keys/ 19 | * 20 | * @package database 21 | */ 22 | final class ForeignKey extends ConstraintKey 23 | { 24 | /** 25 | * The change is allowed and propagates on the child table. 26 | * For example, if a parent row is deleted, the child row is also deleted; 27 | * if a parent row's ID changes, the child row's ID will also change. 28 | * 29 | * @var string 30 | */ 31 | public const OPT_CASCADE = 'CASCADE'; 32 | /** 33 | * Synonym for RESTRICT. 34 | * 35 | * @see ForeignKey::OPT_RESTRICT 36 | * 37 | * @var string 38 | */ 39 | public const OPT_NO_ACTION = 'NO ACTION'; 40 | /** 41 | * The change on the parent table is prevented. 42 | * The statement terminates with a 1451 error (SQLSTATE '2300'). 43 | * This is the default behavior for both ON DELETE and ON UPDATE. 44 | * 45 | * @var string 46 | */ 47 | public const OPT_RESTRICT = 'RESTRICT'; 48 | /** 49 | * The change is allowed, and the child row's foreign key columns are set 50 | * to NULL. 51 | * 52 | * @var string 53 | */ 54 | public const OPT_SET_NULL = 'SET NULL'; 55 | protected string $type = 'FOREIGN KEY'; 56 | protected ?string $referenceTable = null; 57 | /** 58 | * @var array 59 | */ 60 | protected array $referenceColumns = []; 61 | protected ?string $onDelete = null; 62 | protected ?string $onUpdate = null; 63 | 64 | /** 65 | * @param string $table 66 | * @param string $column 67 | * @param string ...$columns 68 | * 69 | * @return static 70 | */ 71 | public function references(string $table, string $column, string ...$columns) : static 72 | { 73 | $this->referenceTable = $table; 74 | $this->referenceColumns = $columns ? \array_merge([$column], $columns) : [$column]; 75 | return $this; 76 | } 77 | 78 | protected function renderReferences() : string 79 | { 80 | if ($this->referenceTable === null) { 81 | throw new LogicException('REFERENCES clause was not set'); 82 | } 83 | $table = $this->database->protectIdentifier($this->referenceTable); 84 | $columns = []; 85 | foreach ($this->referenceColumns as $column) { 86 | $columns[] = $this->database->protectIdentifier($column); 87 | } 88 | $columns = \implode(', ', $columns); 89 | return " REFERENCES {$table} ({$columns})"; 90 | } 91 | 92 | /** 93 | * @param string $option 94 | * 95 | * @return static 96 | */ 97 | public function onDelete(string $option) : static 98 | { 99 | $this->onDelete = $option; 100 | return $this; 101 | } 102 | 103 | protected function renderOnDelete() : ?string 104 | { 105 | if ($this->onDelete === null) { 106 | return null; 107 | } 108 | $reference = $this->makeReferenceOption($this->onDelete); 109 | return " ON DELETE {$reference}"; 110 | } 111 | 112 | /** 113 | * @param string $option 114 | * 115 | * @return static 116 | */ 117 | public function onUpdate(string $option) : static 118 | { 119 | $this->onUpdate = $option; 120 | return $this; 121 | } 122 | 123 | protected function renderOnUpdate() : ?string 124 | { 125 | if ($this->onUpdate === null) { 126 | return null; 127 | } 128 | $reference = $this->makeReferenceOption($this->onUpdate); 129 | return " ON UPDATE {$reference}"; 130 | } 131 | 132 | private function makeReferenceOption(string $option) : string 133 | { 134 | $result = \strtoupper($option); 135 | if (\in_array($result, ['RESTRICT', 'CASCADE', 'SET NULL', 'NO ACTION'], true)) { 136 | return $result; 137 | } 138 | throw new InvalidArgumentException("Invalid reference option: {$option}"); 139 | } 140 | 141 | protected function renderTypeAttributes() : ?string 142 | { 143 | return $this->renderReferences() . $this->renderOnDelete() . $this->renderOnUpdate(); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/Manipulation/Update.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Manipulation; 11 | 12 | use Closure; 13 | use InvalidArgumentException; 14 | use LogicException; 15 | 16 | /** 17 | * Class Update. 18 | * 19 | * @see https://mariadb.com/kb/en/update/ 20 | * 21 | * @package database 22 | */ 23 | class Update extends Statement 24 | { 25 | use Traits\Join; 26 | use Traits\Set; 27 | use Traits\Where; 28 | use Traits\OrderBy; 29 | 30 | /** 31 | * Convert errors to warnings, which will not stop inserts of additional rows. 32 | * 33 | * @see https://mariadb.com/kb/en/insert-ignore/ 34 | * 35 | * @var string 36 | */ 37 | public const OPT_IGNORE = 'IGNORE'; 38 | /** 39 | * @see https://mariadb.com/kb/en/high_priority-and-low_priority/ 40 | * 41 | * @var string 42 | */ 43 | public const OPT_LOW_PRIORITY = 'LOW_PRIORITY'; 44 | 45 | protected function renderOptions() : ?string 46 | { 47 | if ( ! $this->hasOptions()) { 48 | return null; 49 | } 50 | $options = $this->sql['options']; 51 | foreach ($options as &$option) { 52 | $input = $option; 53 | $option = \strtoupper($option); 54 | if ( ! \in_array($option, [ 55 | static::OPT_IGNORE, 56 | static::OPT_LOW_PRIORITY, 57 | ], true)) { 58 | throw new InvalidArgumentException("Invalid option: {$input}"); 59 | } 60 | } 61 | unset($option); 62 | $options = \implode(' ', $options); 63 | return " {$options}"; 64 | } 65 | 66 | /** 67 | * Sets the table references. 68 | * 69 | * @param array|Closure|string $reference 70 | * @param array|Closure|string ...$references 71 | * 72 | * @return static 73 | */ 74 | public function table( 75 | array | Closure | string $reference, 76 | array | Closure | string ...$references 77 | ) : static { 78 | $this->sql['table'] = []; 79 | foreach ([$reference, ...$references] as $reference) { 80 | $this->sql['table'][] = $reference; 81 | } 82 | return $this; 83 | } 84 | 85 | protected function renderTable() : string 86 | { 87 | if ( ! isset($this->sql['table'])) { 88 | throw new LogicException('Table references must be set'); 89 | } 90 | $tables = []; 91 | foreach ($this->sql['table'] as $table) { 92 | $tables[] = $this->renderAliasedIdentifier($table); 93 | } 94 | return ' ' . \implode(', ', $tables); 95 | } 96 | 97 | /** 98 | * Sets the LIMIT clause. 99 | * 100 | * @param int $limit 101 | * 102 | * @see https://mariadb.com/kb/en/limit/ 103 | * 104 | * @return static 105 | */ 106 | public function limit(int $limit) : static 107 | { 108 | return $this->setLimit($limit); 109 | } 110 | 111 | protected function renderSetPart() : string 112 | { 113 | if ( ! $this->hasSet()) { 114 | throw new LogicException('SET statement must be set'); 115 | } 116 | return $this->renderSet(); 117 | } 118 | 119 | /** 120 | * Renders the UPDATE statement. 121 | * 122 | * @return string 123 | */ 124 | public function sql() : string 125 | { 126 | $sql = 'UPDATE' . \PHP_EOL; 127 | $part = $this->renderOptions(); 128 | if ($part) { 129 | $sql .= $part . \PHP_EOL; 130 | } 131 | $sql .= $this->renderTable() . \PHP_EOL; 132 | $part = $this->renderJoin(); 133 | if ($part) { 134 | $sql .= $part . \PHP_EOL; 135 | } 136 | $sql .= $this->renderSetPart() . \PHP_EOL; 137 | $part = $this->renderWhere(); 138 | if ($part) { 139 | $sql .= $part . \PHP_EOL; 140 | } 141 | $part = $this->renderOrderBy(); 142 | if ($part) { 143 | $sql .= $part . \PHP_EOL; 144 | } 145 | $part = $this->renderLimit(); 146 | if ($part) { 147 | $sql .= $part . \PHP_EOL; 148 | } 149 | return $sql; 150 | } 151 | 152 | /** 153 | * Runs the UPDATE statement. 154 | * 155 | * @return int|string The number of affected rows 156 | */ 157 | public function run() : int|string 158 | { 159 | return $this->database->exec($this->sql()); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/Manipulation/Delete.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Manipulation; 11 | 12 | use Closure; 13 | use InvalidArgumentException; 14 | 15 | /** 16 | * Class Delete. 17 | * 18 | * @see https://mariadb.com/kb/en/delete/ 19 | * 20 | * @package database 21 | */ 22 | class Delete extends Statement 23 | { 24 | use Traits\Join; 25 | use Traits\OrderBy; 26 | use Traits\Where; 27 | 28 | /** 29 | * @var string 30 | */ 31 | public const OPT_LOW_PRIORITY = 'LOW_PRIORITY'; 32 | /** 33 | * @var string 34 | */ 35 | public const OPT_QUICK = 'QUICK'; 36 | /** 37 | * @var string 38 | */ 39 | public const OPT_IGNORE = 'IGNORE'; 40 | 41 | protected function renderOptions() : ?string 42 | { 43 | if ( ! $this->hasOptions()) { 44 | return null; 45 | } 46 | $options = $this->sql['options']; 47 | foreach ($options as &$option) { 48 | $input = $option; 49 | $option = \strtoupper($option); 50 | if ( ! \in_array($option, [ 51 | static::OPT_LOW_PRIORITY, 52 | static::OPT_QUICK, 53 | static::OPT_IGNORE, 54 | ], true)) { 55 | throw new InvalidArgumentException("Invalid option: {$input}"); 56 | } 57 | } 58 | unset($option); 59 | $options = \implode(' ', $options); 60 | return " {$options}"; 61 | } 62 | 63 | /** 64 | * Sets the table references. 65 | * 66 | * @param array|Closure|string $reference The table 67 | * name as string, a subquery as Closure or an array for aliased table where 68 | * the key is the alias name and the value is the table name or a subquery 69 | * @param array|Closure|string ...$references Extra 70 | * references. Same values as $reference 71 | * 72 | * @return static 73 | */ 74 | public function table( 75 | array | Closure | string $reference, 76 | array | Closure | string ...$references 77 | ) : static { 78 | $this->sql['table'] = []; 79 | foreach ([$reference, ...$references] as $reference) { 80 | $this->sql['table'][] = $reference; 81 | } 82 | return $this; 83 | } 84 | 85 | /** 86 | * Renders the table references. 87 | * 88 | * @return string|null The table references or null if none was set 89 | */ 90 | protected function renderTable() : ?string 91 | { 92 | if ( ! isset($this->sql['table'])) { 93 | return null; 94 | } 95 | $tables = []; 96 | foreach ($this->sql['table'] as $table) { 97 | $tables[] = $this->renderAliasedIdentifier($table); 98 | } 99 | return ' ' . \implode(', ', $tables); 100 | } 101 | 102 | /** 103 | * Sets the LIMIT clause. 104 | * 105 | * @param int $limit 106 | * 107 | * @see https://mariadb.com/kb/en/limit/ 108 | * 109 | * @return static 110 | */ 111 | public function limit(int $limit) : static 112 | { 113 | return $this->setLimit($limit); 114 | } 115 | 116 | /** 117 | * Renders de DELETE statement. 118 | * 119 | * @return string 120 | */ 121 | public function sql() : string 122 | { 123 | $sql = 'DELETE' . \PHP_EOL; 124 | $part = $this->renderOptions(); 125 | if ($part) { 126 | $sql .= $part . \PHP_EOL; 127 | } 128 | $part = $this->renderTable(); 129 | if ($part) { 130 | $sql .= $part . \PHP_EOL; 131 | } 132 | $part = $this->renderFrom(); 133 | if ($part) { 134 | $sql .= $part . \PHP_EOL; 135 | } 136 | $part = $this->renderJoin(); 137 | if ($part) { 138 | $sql .= $part . \PHP_EOL; 139 | } 140 | $part = $this->renderWhere(); 141 | if ($part) { 142 | $sql .= $part . \PHP_EOL; 143 | } 144 | $part = $this->renderOrderBy(); 145 | if ($part) { 146 | $sql .= $part . \PHP_EOL; 147 | } 148 | $part = $this->renderLimit(); 149 | if ($part) { 150 | $sql .= $part . \PHP_EOL; 151 | } 152 | return $sql; 153 | } 154 | 155 | /** 156 | * Runs the DELETE statement. 157 | * 158 | * @return int|string The number of affected rows 159 | */ 160 | public function run() : int|string 161 | { 162 | return $this->database->exec($this->sql()); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/Debug/DatabaseCollector.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Debug; 11 | 12 | use Framework\Database\Database; 13 | use Framework\Debug\Collector; 14 | 15 | /** 16 | * Class DatabaseCollector. 17 | * 18 | * @package database 19 | */ 20 | class DatabaseCollector extends Collector 21 | { 22 | protected Database $database; 23 | /** 24 | * @var string 25 | * 26 | * @deprecated Use {@see Database::getConnection()} 27 | */ 28 | protected string $serverInfo; 29 | 30 | public function setDatabase(Database $database) : static 31 | { 32 | $this->database = $database; 33 | return $this; 34 | } 35 | 36 | /** 37 | * @param string $serverInfo 38 | * 39 | * @deprecated Use {@see Database::getConnection()} 40 | * 41 | * @codeCoverageIgnore 42 | */ 43 | public function setServerInfo(string $serverInfo) : void 44 | { 45 | \trigger_error( 46 | 'Method ' . __METHOD__ . ' is deprecated', 47 | \E_USER_DEPRECATED 48 | ); 49 | $this->serverInfo = $serverInfo; 50 | } 51 | 52 | public function getServerInfo() : string 53 | { 54 | return $this->database->getConnection()->server_info; 55 | } 56 | 57 | public function getActivities() : array 58 | { 59 | $activities = []; 60 | foreach ($this->getData() as $index => $data) { 61 | $activities[] = [ 62 | 'collector' => $this->getName(), 63 | 'class' => static::class, 64 | 'description' => 'Run statement ' . ($index + 1), 65 | 'start' => $data['start'], 66 | 'end' => $data['end'], 67 | ]; 68 | } 69 | return $activities; 70 | } 71 | 72 | public function getContents() : string 73 | { 74 | \ob_start(); 75 | if ( ! isset($this->database)) { 76 | echo '

This collector has not been added to a Database instance.

'; 77 | return \ob_get_clean(); // @phpstan-ignore-line 78 | } 79 | echo $this->showHeader(); 80 | if ( ! $this->hasData()) { 81 | echo '

Did not run statements.

'; 82 | return \ob_get_clean(); // @phpstan-ignore-line 83 | } 84 | $count = \count($this->getData()); ?> 85 |

Ran statement:

86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | getData() as $index => $item): ?> 97 | 98 | 99 | 100 | 105 | > 108 | 109 | 110 | 111 |
#TimeStatementRows
101 |
104 |
112 | database->getConfig(); 119 | \ob_start(); 120 | ?> 121 |

122 | Host: 123 |

124 | getHostInfo(), 'TCP/IP')) { 126 | if (isset($config['port'])) { 127 | ?> 128 |

Port:

129 | 132 |

Socket:

133 | 136 |

Server Info: getServerInfo()) ?>

137 | database->getConnection()->host_info; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/Manipulation/Replace.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Manipulation; 11 | 12 | use InvalidArgumentException; 13 | use LogicException; 14 | 15 | /** 16 | * Class Replace. 17 | * 18 | * @see https://mariadb.com/kb/en/replace/ 19 | * 20 | * @package database 21 | */ 22 | class Replace extends Statement 23 | { 24 | use Traits\Select; 25 | use Traits\Set; 26 | use Traits\Values; 27 | 28 | /** 29 | * @see https://mariadb.com/kb/en/insert-delayed/ 30 | * 31 | * @var string 32 | */ 33 | public const OPT_DELAYED = 'DELAYED'; 34 | /** 35 | * @see https://mariadb.com/kb/en/high_priority-and-low_priority/ 36 | * 37 | * @var string 38 | */ 39 | public const OPT_LOW_PRIORITY = 'LOW_PRIORITY'; 40 | 41 | /** 42 | * @param string $table 43 | * 44 | * @return static 45 | */ 46 | public function into(string $table) : static 47 | { 48 | $this->sql['into'] = $table; 49 | return $this; 50 | } 51 | 52 | protected function renderInto() : string 53 | { 54 | if ( ! isset($this->sql['into'])) { 55 | throw new LogicException('INTO table must be set'); 56 | } 57 | return ' INTO ' . $this->renderIdentifier($this->sql['into']); 58 | } 59 | 60 | /** 61 | * @param string $column 62 | * @param string ...$columns 63 | * 64 | * @return static 65 | */ 66 | public function columns(string $column, string ...$columns) : static 67 | { 68 | $this->sql['columns'] = [$column, ...$columns]; 69 | return $this; 70 | } 71 | 72 | protected function renderColumns() : ?string 73 | { 74 | if ( ! isset($this->sql['columns'])) { 75 | return null; 76 | } 77 | $columns = []; 78 | foreach ($this->sql['columns'] as $column) { 79 | $columns[] = $this->renderIdentifier($column); 80 | } 81 | $columns = \implode(', ', $columns); 82 | return " ({$columns})"; 83 | } 84 | 85 | protected function renderOptions() : ?string 86 | { 87 | if ( ! $this->hasOptions()) { 88 | return null; 89 | } 90 | $options = $this->sql['options']; 91 | foreach ($options as &$option) { 92 | $input = $option; 93 | $option = \strtoupper($option); 94 | if ( ! \in_array($option, [ 95 | static::OPT_DELAYED, 96 | static::OPT_LOW_PRIORITY, 97 | ], true)) { 98 | throw new InvalidArgumentException("Invalid option: {$input}"); 99 | } 100 | } 101 | unset($option); 102 | $intersection = \array_intersect( 103 | $options, 104 | [static::OPT_DELAYED, static::OPT_LOW_PRIORITY] 105 | ); 106 | if (\count($intersection) > 1) { 107 | throw new LogicException( 108 | 'Options LOW_PRIORITY and DELAYED can not be used together' 109 | ); 110 | } 111 | $options = \implode(' ', $options); 112 | return " {$options}"; 113 | } 114 | 115 | protected function checkRowStatementsConflict() : void 116 | { 117 | if ( ! isset($this->sql['values']) 118 | && ! isset($this->sql['select']) 119 | && ! $this->hasSet() 120 | ) { 121 | throw new LogicException( 122 | 'The REPLACE INTO must be followed by VALUES, SET or SELECT statement' 123 | ); 124 | } 125 | } 126 | 127 | /** 128 | * Renders the REPLACE statement. 129 | * 130 | * @return string 131 | */ 132 | public function sql() : string 133 | { 134 | $sql = 'REPLACE' . \PHP_EOL; 135 | $part = $this->renderOptions(); 136 | if ($part) { 137 | $sql .= $part . \PHP_EOL; 138 | } 139 | $sql .= $this->renderInto() . \PHP_EOL; 140 | $part = $this->renderColumns(); 141 | if ($part) { 142 | $sql .= $part . \PHP_EOL; 143 | } 144 | $this->checkRowStatementsConflict(); 145 | $part = $this->renderValues(); 146 | if ($part) { 147 | $sql .= $part . \PHP_EOL; 148 | } 149 | $part = $this->renderSetCheckingConflicts(); 150 | if ($part) { 151 | $sql .= $part . \PHP_EOL; 152 | } 153 | $part = $this->renderSelect(); 154 | if ($part) { 155 | $sql .= $part . \PHP_EOL; 156 | } 157 | return $sql; 158 | } 159 | 160 | /** 161 | * Runs the REPLACE statement. 162 | * 163 | * @return int|string The number of affected rows 164 | */ 165 | public function run() : int | string 166 | { 167 | return $this->database->exec($this->sql()); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/Definition/Table/TableDefinition.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table; 11 | 12 | use Closure; 13 | use Framework\Database\Database; 14 | use Framework\Database\Definition\Table\Columns\ColumnDefinition; 15 | use Framework\Database\Definition\Table\Indexes\IndexDefinition; 16 | 17 | /** 18 | * Class TableDefinition. 19 | * 20 | * @package database 21 | */ 22 | class TableDefinition extends DefinitionPart 23 | { 24 | protected Database $database; 25 | /** 26 | * @var array> 27 | */ 28 | protected array $columns = []; 29 | /** 30 | * @var array> 31 | */ 32 | protected array $indexes = []; 33 | /** 34 | * @var array 35 | */ 36 | protected array $checks = []; 37 | protected ?string $condition = null; 38 | 39 | /** 40 | * TableDefinition constructor. 41 | * 42 | * @param Database $database 43 | * @param string|null $condition 44 | */ 45 | public function __construct(Database $database, string $condition = null) 46 | { 47 | $this->database = $database; 48 | $this->condition = $condition; 49 | } 50 | 51 | /** 52 | * Adds a column to the Table Definition list. 53 | * 54 | * @param string $name Column name 55 | * @param string|null $changeName New column name. Used on ALTER TABLE CHANGE 56 | * 57 | * @return ColumnDefinition 58 | */ 59 | public function column(string $name, string $changeName = null) : ColumnDefinition 60 | { 61 | $definition = new ColumnDefinition($this->database); 62 | $this->columns[] = [ 63 | 'name' => $name, 64 | 'change_name' => $changeName, 65 | 'definition' => $definition, 66 | ]; 67 | return $definition; 68 | } 69 | 70 | /** 71 | * Adds an index to the Table Definition list. 72 | * 73 | * @param string|null $name Index name 74 | * 75 | * @return IndexDefinition 76 | */ 77 | public function index(string $name = null) : IndexDefinition 78 | { 79 | $definition = new IndexDefinition($this->database, $name); 80 | $this->indexes[] = [ 81 | 'name' => $name, 82 | 'definition' => $definition, 83 | ]; 84 | return $definition; 85 | } 86 | 87 | /** 88 | * Adds a check constraint to the Table Definition list. 89 | * 90 | * @param Closure $expression Must return a string with the check expression. 91 | * The function receives a Database instance in the first parameter. 92 | * 93 | * @return Check 94 | */ 95 | public function check(Closure $expression) : Check 96 | { 97 | return $this->checks[] = new Check($this->database, $expression); 98 | } 99 | 100 | protected function renderColumns(string $prefix = null) : string 101 | { 102 | if ($prefix) { 103 | $prefix .= ' COLUMN'; 104 | } 105 | if ($this->condition) { 106 | $prefix .= ' ' . $this->condition; 107 | } 108 | $sql = []; 109 | foreach ($this->columns as $column) { 110 | $name = $this->database->protectIdentifier($column['name']); 111 | $changeName = $column['change_name'] 112 | ? ' ' . $this->database->protectIdentifier($column['change_name']) 113 | : null; 114 | $definition = $column['definition']->sql(); 115 | $sql[] = " {$prefix} {$name}{$changeName}{$definition}"; 116 | } 117 | return \implode(',' . \PHP_EOL, $sql); 118 | } 119 | 120 | protected function renderIndexes(string $prefix = null) : string 121 | { 122 | $sql = []; 123 | foreach ($this->indexes as $index) { 124 | $definition = $index['definition']->sql(); 125 | if ($this->condition) { 126 | $definition = \explode('(', $definition, 2); 127 | $definition = $definition[0] . $this->condition . ' (' . $definition[1]; 128 | } 129 | $sql[] = " {$prefix}{$definition}"; 130 | } 131 | return \implode(',' . \PHP_EOL, $sql); 132 | } 133 | 134 | protected function renderChecks() : string 135 | { 136 | $sql = []; 137 | foreach ($this->checks as $check) { 138 | $sql[] = ' ' . $check->sql(); 139 | } 140 | return \implode(',' . \PHP_EOL, $sql); 141 | } 142 | 143 | protected function sql(string $prefix = null) : string 144 | { 145 | $sql = $this->renderColumns($prefix); 146 | $part = $this->renderIndexes($prefix); 147 | if ($part) { 148 | $sql .= ',' . \PHP_EOL . $part; 149 | } 150 | $part = $this->renderChecks(); 151 | if ($part) { 152 | $sql .= ',' . \PHP_EOL . $part; 153 | } 154 | return $sql; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/Manipulation/Statement.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Manipulation; 11 | 12 | use Closure; 13 | use InvalidArgumentException; 14 | 15 | /** 16 | * Class Statement. 17 | * 18 | * @see https://mariadb.com/kb/en/data-manipulation/ 19 | * 20 | * @package database 21 | */ 22 | abstract class Statement extends \Framework\Database\Statement 23 | { 24 | /** 25 | * Sets the statement options. 26 | * 27 | * @param string $option One of the OPT_* constants 28 | * @param string ...$options Each option value must be one of the OPT_* constants 29 | * 30 | * @return static 31 | */ 32 | public function options(string $option, string ...$options) : static 33 | { 34 | $this->sql['options'] = []; 35 | foreach ([$option, ...$options] as $option) { 36 | $this->sql['options'][] = $option; 37 | } 38 | return $this; 39 | } 40 | 41 | /** 42 | * Tells if the statement has options set. 43 | * 44 | * @return bool 45 | */ 46 | protected function hasOptions() : bool 47 | { 48 | return isset($this->sql['options']); 49 | } 50 | 51 | abstract protected function renderOptions() : ?string; 52 | 53 | /** 54 | * Returns an SQL part between parentheses. 55 | * 56 | * @param Closure $subquery A {@see Closure} having the current Manipulation 57 | * instance as first argument. The returned value must be scalar 58 | * 59 | * @see https://mariadb.com/kb/en/subqueries/ 60 | * @see https://mariadb.com/kb/en/built-in-functions/ 61 | * 62 | * @return string 63 | */ 64 | protected function subquery(Closure $subquery) : string 65 | { 66 | return '(' . $subquery($this->database) . ')'; 67 | } 68 | 69 | /** 70 | * Sets the LIMIT clause. 71 | * 72 | * @param int $limit 73 | * @param int|null $offset 74 | * 75 | * @see https://mariadb.com/kb/en/limit/ 76 | * 77 | * @return static 78 | */ 79 | protected function setLimit(int $limit, int $offset = null) : static 80 | { 81 | $this->sql['limit'] = [ 82 | 'limit' => $limit, 83 | 'offset' => $offset, 84 | ]; 85 | return $this; 86 | } 87 | 88 | /** 89 | * Renders the LIMIT clause. 90 | * 91 | * @return string|null 92 | */ 93 | protected function renderLimit() : ?string 94 | { 95 | if ( ! isset($this->sql['limit'])) { 96 | return null; 97 | } 98 | if ($this->sql['limit']['limit'] < 1) { 99 | throw new InvalidArgumentException('LIMIT must be greater than 0'); 100 | } 101 | $offset = $this->sql['limit']['offset']; 102 | if ($offset !== null) { 103 | if ($offset < 1) { 104 | throw new InvalidArgumentException('LIMIT OFFSET must be greater than 0'); 105 | } 106 | $offset = " OFFSET {$this->sql['limit']['offset']}"; 107 | } 108 | return " LIMIT {$this->sql['limit']['limit']}{$offset}"; 109 | } 110 | 111 | /** 112 | * Renders a column part. 113 | * 114 | * @param Closure|string $column The column name or a subquery 115 | * 116 | * @return string 117 | */ 118 | protected function renderIdentifier(Closure | string $column) : string 119 | { 120 | return $column instanceof Closure 121 | ? $this->subquery($column) 122 | : $this->database->protectIdentifier($column); 123 | } 124 | 125 | /** 126 | * Renders a column part with an optional alias name, AS clause. 127 | * 128 | * @param array|Closure|string $column The column name, 129 | * a subquery or an array where the index is the alias and the value is the column/subquery 130 | * 131 | * @return string 132 | */ 133 | protected function renderAliasedIdentifier(array | Closure | string $column) : string 134 | { 135 | if (\is_array($column)) { 136 | if (\count($column) !== 1) { 137 | throw new InvalidArgumentException('Aliased column must have only 1 key'); 138 | } 139 | $alias = (string) \array_key_first($column); 140 | return $this->renderIdentifier($column[$alias]) . ' AS ' 141 | . $this->database->protectIdentifier($alias); 142 | } 143 | return $this->renderIdentifier($column); 144 | } 145 | 146 | /** 147 | * Renders a subquery or quote a value. 148 | * 149 | * @param Closure|float|int|string|null $value A {@see Closure} for 150 | * subquery, other types to quote 151 | * 152 | * @return float|int|string 153 | */ 154 | protected function renderValue(Closure | float | int | string | null $value) : float | int | string 155 | { 156 | return $value instanceof Closure 157 | ? $this->subquery($value) 158 | : $this->database->quote($value); 159 | } 160 | 161 | /** 162 | * Renders an assignment part. 163 | * 164 | * @param string $identifier Identifier/column name 165 | * @param Closure|float|int|string|null $expression Expression/value 166 | * 167 | * @see Statement::renderValue() 168 | * @see https://mariadb.com/kb/en/assignment-operators-assignment-operator/ 169 | * 170 | * @return string 171 | */ 172 | protected function renderAssignment( 173 | string $identifier, 174 | Closure | float | int | string | null $expression 175 | ) : string { 176 | return $this->database->protectIdentifier($identifier) 177 | . ' = ' . $this->renderValue($expression); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/Column.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns; 11 | 12 | use Closure; 13 | use Framework\Database\Database; 14 | use Framework\Database\Definition\Table\DefinitionPart; 15 | use LogicException; 16 | 17 | /** 18 | * Class Column. 19 | * 20 | * @package database 21 | */ 22 | abstract class Column extends DefinitionPart 23 | { 24 | protected Database $database; 25 | protected string $type; 26 | /** 27 | * @var array 28 | */ 29 | protected array $length; 30 | protected bool $null = false; 31 | protected bool $uniqueKey = false; 32 | protected bool $primaryKey = false; 33 | protected bool | Closure | float | int | string | null $default; 34 | protected Closure $check; 35 | protected ?string $comment; 36 | protected bool $first = false; 37 | protected ?string $after; 38 | 39 | /** 40 | * Column constructor. 41 | * 42 | * @param Database $database 43 | * @param bool|float|int|string|null ...$length 44 | */ 45 | public function __construct(Database $database, bool | float | int | string | null ...$length) 46 | { 47 | $this->database = $database; 48 | $this->length = $length; 49 | } 50 | 51 | protected function renderType() : string 52 | { 53 | if (empty($this->type)) { 54 | throw new LogicException('Column type is empty'); 55 | } 56 | return ' ' . $this->type; 57 | } 58 | 59 | protected function renderLength() : ?string 60 | { 61 | if ( ! isset($this->length[0])) { 62 | return null; 63 | } 64 | $length = $this->database->quote($this->length[0]); 65 | return "({$length})"; 66 | } 67 | 68 | /** 69 | * @param Closure $expression 70 | * 71 | * @return static 72 | */ 73 | public function check(Closure $expression) : static 74 | { 75 | $this->check = $expression; 76 | return $this; 77 | } 78 | 79 | protected function renderCheck() : ?string 80 | { 81 | if ( ! isset($this->check)) { 82 | return null; 83 | } 84 | return ' CHECK (' . ($this->check)($this->database) . ')'; 85 | } 86 | 87 | /** 88 | * @return static 89 | */ 90 | public function null() : static 91 | { 92 | $this->null = true; 93 | return $this; 94 | } 95 | 96 | /** 97 | * @return static 98 | */ 99 | public function notNull() : static 100 | { 101 | $this->null = false; 102 | return $this; 103 | } 104 | 105 | protected function renderNull() : ?string 106 | { 107 | return $this->null ? ' NULL' : ' NOT NULL'; 108 | } 109 | 110 | /** 111 | * @param bool|Closure|float|int|string|null $default 112 | * 113 | * @return static 114 | */ 115 | public function default(bool | Closure | float | int | string | null $default) : static 116 | { 117 | $this->default = $default; 118 | return $this; 119 | } 120 | 121 | protected function renderDefault() : ?string 122 | { 123 | if ( ! isset($this->default)) { 124 | return null; 125 | } 126 | $default = $this->default instanceof Closure 127 | ? '(' . ($this->default)($this->database) . ')' 128 | : $this->database->quote($this->default); 129 | return ' DEFAULT ' . $default; 130 | } 131 | 132 | /** 133 | * @param string $comment 134 | * 135 | * @return static 136 | */ 137 | public function comment(string $comment) : static 138 | { 139 | $this->comment = $comment; 140 | return $this; 141 | } 142 | 143 | protected function renderComment() : ?string 144 | { 145 | if ( ! isset($this->comment)) { 146 | return null; 147 | } 148 | return ' COMMENT ' . $this->database->quote($this->comment); 149 | } 150 | 151 | /** 152 | * @return static 153 | */ 154 | public function primaryKey() : static 155 | { 156 | $this->primaryKey = true; 157 | return $this; 158 | } 159 | 160 | protected function renderPrimaryKey() : ?string 161 | { 162 | if ( ! $this->primaryKey) { 163 | return null; 164 | } 165 | return ' PRIMARY KEY'; 166 | } 167 | 168 | /** 169 | * @return static 170 | */ 171 | public function uniqueKey() : static 172 | { 173 | $this->uniqueKey = true; 174 | return $this; 175 | } 176 | 177 | protected function renderUniqueKey() : ?string 178 | { 179 | if ( ! $this->uniqueKey) { 180 | return null; 181 | } 182 | return ' UNIQUE KEY'; 183 | } 184 | 185 | /** 186 | * @return static 187 | */ 188 | public function first() : static 189 | { 190 | $this->first = true; 191 | return $this; 192 | } 193 | 194 | protected function renderFirst() : ?string 195 | { 196 | if ( ! $this->first) { 197 | return null; 198 | } 199 | return ' FIRST'; 200 | } 201 | 202 | /** 203 | * @param string $column 204 | * 205 | * @return static 206 | */ 207 | public function after(string $column) : static 208 | { 209 | $this->after = $column; 210 | return $this; 211 | } 212 | 213 | protected function renderAfter() : ?string 214 | { 215 | if ( ! isset($this->after)) { 216 | return null; 217 | } 218 | if ($this->first) { 219 | throw new LogicException('Clauses FIRST and AFTER can not be used together'); 220 | } 221 | return ' AFTER ' . $this->database->protectIdentifier($this->after); 222 | } 223 | 224 | protected function renderTypeAttributes() : ?string 225 | { 226 | return null; 227 | } 228 | 229 | protected function sql() : string 230 | { 231 | $sql = $this->renderType(); 232 | $sql .= $this->renderLength(); 233 | $sql .= $this->renderTypeAttributes(); 234 | $sql .= $this->renderNull(); 235 | $sql .= $this->renderDefault(); 236 | $sql .= $this->renderUniqueKey(); 237 | $sql .= $this->renderPrimaryKey(); 238 | $sql .= $this->renderComment(); 239 | $sql .= $this->renderFirst(); 240 | $sql .= $this->renderAfter(); 241 | $sql .= $this->renderCheck(); 242 | return $sql; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/Result.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database; 11 | 12 | use Framework\Database\Result\Field; 13 | use LogicException; 14 | use mysqli_result; 15 | use OutOfBoundsException; 16 | use OutOfRangeException; 17 | 18 | /** 19 | * Class Result. 20 | * 21 | * @package database 22 | */ 23 | class Result 24 | { 25 | /** 26 | * @var mysqli_result 27 | */ 28 | protected mysqli_result $result; 29 | protected bool $buffered; 30 | protected bool $free = false; 31 | protected string $fetchClass = \stdClass::class; 32 | /** 33 | * @var array 34 | */ 35 | protected array $fetchConstructor = []; 36 | 37 | /** 38 | * Result constructor. 39 | * 40 | * @param mysqli_result $result 41 | * @param bool $buffered 42 | */ 43 | public function __construct(mysqli_result $result, bool $buffered) 44 | { 45 | $this->result = $result; 46 | $this->buffered = $buffered; 47 | } 48 | 49 | public function __destruct() 50 | { 51 | if ( ! $this->isFree()) { 52 | $this->free(); 53 | } 54 | } 55 | 56 | /** 57 | * Frees the memory associated with a result. 58 | */ 59 | public function free() : void 60 | { 61 | $this->checkIsFree(); 62 | $this->free = true; 63 | $this->result->free(); 64 | } 65 | 66 | public function isFree() : bool 67 | { 68 | return $this->free; 69 | } 70 | 71 | protected function checkIsFree() : void 72 | { 73 | if ($this->isFree()) { 74 | throw new LogicException('Result is already free'); 75 | } 76 | } 77 | 78 | public function isBuffered() : bool 79 | { 80 | return $this->buffered; 81 | } 82 | 83 | /** 84 | * Adjusts the result pointer to an arbitrary row in the result. 85 | * 86 | * @param int $offset The field offset. Must be between zero and the total 87 | * number of rows minus one 88 | * 89 | * @throws LogicException if is an unbuffered result 90 | * @throws OutOfBoundsException for invalid cursor offset 91 | * 92 | * @return bool 93 | */ 94 | public function moveCursor(int $offset) : bool 95 | { 96 | $this->checkIsFree(); 97 | if ( ! $this->isBuffered()) { 98 | throw new LogicException('Cursor cannot be moved on unbuffered results'); 99 | } 100 | if ($offset < 0 || ($offset !== 0 && $offset >= $this->result->num_rows)) { 101 | throw new OutOfRangeException( 102 | "Invalid cursor offset: {$offset}" 103 | ); 104 | } 105 | return $this->result->data_seek($offset); 106 | } 107 | 108 | /** 109 | * @param string $class 110 | * @param mixed ...$constructor 111 | * 112 | * @return static 113 | */ 114 | public function setFetchClass(string $class, mixed ...$constructor) : static 115 | { 116 | $this->fetchClass = $class; 117 | $this->fetchConstructor = $constructor; 118 | return $this; 119 | } 120 | 121 | /** 122 | * Fetches the current row as object and move the cursor to the next. 123 | * 124 | * @param string|null $class 125 | * @param mixed ...$constructor 126 | * 127 | * @return object|null 128 | */ 129 | public function fetch(string $class = null, mixed ...$constructor) : object | null 130 | { 131 | $this->checkIsFree(); 132 | $class ??= $this->fetchClass; 133 | $constructor = $constructor ?: $this->fetchConstructor; 134 | if ($constructor) { 135 | return $this->result->fetch_object($class, $constructor); 136 | } 137 | return $this->result->fetch_object($class); 138 | } 139 | 140 | /** 141 | * Fetches all rows as objects. 142 | * 143 | * @param string|null $class 144 | * @param mixed ...$constructor 145 | * 146 | * @return array 147 | */ 148 | public function fetchAll(string $class = null, mixed ...$constructor) : array 149 | { 150 | $this->checkIsFree(); 151 | $all = []; 152 | while ($row = $this->fetch($class, ...$constructor)) { 153 | $all[] = $row; 154 | } 155 | return $all; 156 | } 157 | 158 | /** 159 | * Fetches a specific row as object and move the cursor to the next. 160 | * 161 | * @param int $offset 162 | * @param string|null $class 163 | * @param mixed ...$constructor 164 | * 165 | * @return object|null 166 | */ 167 | public function fetchRow(int $offset, string $class = null, mixed ...$constructor) : object | null 168 | { 169 | $this->checkIsFree(); 170 | $this->moveCursor($offset); 171 | return $this->fetch($class, ...$constructor); 172 | } 173 | 174 | /** 175 | * Fetches the current row as array and move the cursor to the next. 176 | * 177 | * @return array|null 178 | */ 179 | public function fetchArray() : ?array 180 | { 181 | $this->checkIsFree(); 182 | return $this->result->fetch_assoc(); 183 | } 184 | 185 | /** 186 | * Fetches all rows as arrays. 187 | * 188 | * @return array> 189 | */ 190 | public function fetchArrayAll() : array 191 | { 192 | $this->checkIsFree(); 193 | return $this->result->fetch_all(\MYSQLI_ASSOC); 194 | } 195 | 196 | /** 197 | * Fetches a specific row as array and move the cursor to the next. 198 | * 199 | * @param int $offset 200 | * 201 | * @return array 202 | */ 203 | public function fetchArrayRow(int $offset) : array 204 | { 205 | $this->checkIsFree(); 206 | $this->moveCursor($offset); 207 | return $this->result->fetch_assoc(); 208 | } 209 | 210 | /** 211 | * Gets the number of rows in the result set. 212 | * 213 | * @return int|string 214 | */ 215 | public function numRows() : int | string 216 | { 217 | $this->checkIsFree(); 218 | return $this->result->num_rows; 219 | } 220 | 221 | /** 222 | * Returns an array of objects representing the fields in a result set. 223 | * 224 | * @return array|false an array of objects which contains field 225 | * definition information or false if no field information is available 226 | */ 227 | public function fetchFields() : array | false 228 | { 229 | $this->checkIsFree(); 230 | $fields = $this->result->fetch_fields(); 231 | if ($fields === false) { // @phpstan-ignore-line 232 | return false; 233 | } 234 | foreach ($fields as &$field) { 235 | $field = new Field($field); 236 | } 237 | return $fields; 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/Manipulation/Insert.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Manipulation; 11 | 12 | use Closure; 13 | use InvalidArgumentException; 14 | use LogicException; 15 | 16 | /** 17 | * Class Insert. 18 | * 19 | * @see https://mariadb.com/kb/en/insert/ 20 | * 21 | * @package database 22 | */ 23 | class Insert extends Statement 24 | { 25 | use Traits\Select; 26 | use Traits\Set; 27 | use Traits\Values; 28 | 29 | /** 30 | * @see https://mariadb.com/kb/en/insert-delayed/ 31 | * 32 | * @var string 33 | */ 34 | public const OPT_DELAYED = 'DELAYED'; 35 | /** 36 | * Convert errors to warnings, which will not stop inserts of additional rows. 37 | * 38 | * @see https://mariadb.com/kb/en/insert-ignore/ 39 | * 40 | * @var string 41 | */ 42 | public const OPT_IGNORE = 'IGNORE'; 43 | /** 44 | * @see https://mariadb.com/kb/en/high_priority-and-low_priority/ 45 | * 46 | * @var string 47 | */ 48 | public const OPT_HIGH_PRIORITY = 'HIGH_PRIORITY'; 49 | /** 50 | * @see https://mariadb.com/kb/en/high_priority-and-low_priority/ 51 | * 52 | * @var string 53 | */ 54 | public const OPT_LOW_PRIORITY = 'LOW_PRIORITY'; 55 | 56 | protected function renderOptions() : ?string 57 | { 58 | if ( ! $this->hasOptions()) { 59 | return null; 60 | } 61 | $options = $this->sql['options']; 62 | foreach ($options as &$option) { 63 | $input = $option; 64 | $option = \strtoupper($option); 65 | if ( ! \in_array($option, [ 66 | static::OPT_DELAYED, 67 | static::OPT_IGNORE, 68 | static::OPT_LOW_PRIORITY, 69 | static::OPT_HIGH_PRIORITY, 70 | ], true)) { 71 | throw new InvalidArgumentException("Invalid option: {$input}"); 72 | } 73 | } 74 | unset($option); 75 | $intersection = \array_intersect( 76 | $options, 77 | [static::OPT_DELAYED, static::OPT_HIGH_PRIORITY, static::OPT_LOW_PRIORITY] 78 | ); 79 | if (\count($intersection) > 1) { 80 | throw new LogicException( 81 | 'Options LOW_PRIORITY, DELAYED or HIGH_PRIORITY can not be used together' 82 | ); 83 | } 84 | $options = \implode(' ', $options); 85 | return " {$options}"; 86 | } 87 | 88 | /** 89 | * Sets the INTO table. 90 | * 91 | * @param string $table Table name 92 | * 93 | * @return static 94 | */ 95 | public function into(string $table) : static 96 | { 97 | $this->sql['into'] = $table; 98 | return $this; 99 | } 100 | 101 | /** 102 | * Renders the "INTO $table" clause. 103 | * 104 | * @throws LogicException if INTO was not set 105 | * 106 | * @return string 107 | */ 108 | protected function renderInto() : string 109 | { 110 | if ( ! isset($this->sql['into'])) { 111 | throw new LogicException('INTO table must be set'); 112 | } 113 | return ' INTO ' . $this->renderIdentifier($this->sql['into']); 114 | } 115 | 116 | /** 117 | * Sets the INTO columns. 118 | * 119 | * @param string $column Column name 120 | * @param string ...$columns Extra column names 121 | * 122 | * @return static 123 | */ 124 | public function columns(string $column, string ...$columns) : static 125 | { 126 | $this->sql['columns'] = [$column, ...$columns]; 127 | return $this; 128 | } 129 | 130 | /** 131 | * Renders the INTO $table "(...$columns)" part. 132 | * 133 | * @return string|null The imploded columns or null if none was set 134 | */ 135 | protected function renderColumns() : ?string 136 | { 137 | if ( ! isset($this->sql['columns'])) { 138 | return null; 139 | } 140 | $columns = []; 141 | foreach ($this->sql['columns'] as $column) { 142 | $columns[] = $this->renderIdentifier($column); 143 | } 144 | $columns = \implode(', ', $columns); 145 | return " ({$columns})"; 146 | } 147 | 148 | /** 149 | * Sets the ON DUPLICATE KEY UPDATE part. 150 | * 151 | * @param array|object $columns Column name 152 | * as key/property, column value/expression as value 153 | * 154 | * @see https://mariadb.com/kb/en/insert-on-duplicate-key-update/ 155 | * 156 | * @return static 157 | */ 158 | public function onDuplicateKeyUpdate(array | object $columns) : static 159 | { 160 | $this->sql['on_duplicate'] = (array) $columns; 161 | return $this; 162 | } 163 | 164 | /** 165 | * Renders the ON DUPLICATE KEY UPDATE part. 166 | * 167 | * @return string|null The part or null if it was not set 168 | */ 169 | protected function renderOnDuplicateKeyUpdate() : ?string 170 | { 171 | if ( ! isset($this->sql['on_duplicate'])) { 172 | return null; 173 | } 174 | $onDuplicate = []; 175 | foreach ($this->sql['on_duplicate'] as $column => $value) { 176 | $onDuplicate[] = $this->renderAssignment($column, $value); 177 | } 178 | $onDuplicate = \implode(', ', $onDuplicate); 179 | return " ON DUPLICATE KEY UPDATE {$onDuplicate}"; 180 | } 181 | 182 | /** 183 | * Check for conflicts in the INSERT statement. 184 | * 185 | * @throws LogicException if it has conflicts 186 | */ 187 | protected function checkRowStatementsConflict() : void 188 | { 189 | if ( ! isset($this->sql['values']) 190 | && ! isset($this->sql['select']) 191 | && ! $this->hasSet() 192 | ) { 193 | throw new LogicException( 194 | 'The INSERT INTO must be followed by VALUES, SET or SELECT statement' 195 | ); 196 | } 197 | } 198 | 199 | /** 200 | * Renders the INSERT statement. 201 | * 202 | * @return string 203 | */ 204 | public function sql() : string 205 | { 206 | $sql = 'INSERT' . \PHP_EOL; 207 | $part = $this->renderOptions(); 208 | if ($part) { 209 | $sql .= $part . \PHP_EOL; 210 | } 211 | $sql .= $this->renderInto() . \PHP_EOL; 212 | $part = $this->renderColumns(); 213 | if ($part) { 214 | $sql .= $part . \PHP_EOL; 215 | } 216 | $this->checkRowStatementsConflict(); 217 | $part = $this->renderValues(); 218 | if ($part) { 219 | $sql .= $part . \PHP_EOL; 220 | } 221 | $part = $this->renderSetCheckingConflicts(); 222 | if ($part) { 223 | $sql .= $part . \PHP_EOL; 224 | } 225 | $part = $this->renderSelect(); 226 | if ($part) { 227 | $sql .= $part . \PHP_EOL; 228 | } 229 | $part = $this->renderOnDuplicateKeyUpdate(); 230 | if ($part) { 231 | $sql .= $part . \PHP_EOL; 232 | } 233 | return $sql; 234 | } 235 | 236 | /** 237 | * Runs the INSERT statement. 238 | * 239 | * @return int|string The number of affected rows 240 | */ 241 | public function run() : int|string 242 | { 243 | return $this->database->exec($this->sql()); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/Result/Field.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Result; 11 | 12 | use Error; 13 | 14 | /** 15 | * Class Field. 16 | * 17 | * @property-read string $name The name of the column 18 | * @property-read string $orgname Original column name if an alias was specified 19 | * @property-read string $table The name of the table this field belongs to (if not calculated) 20 | * @property-read string $orgtable Original table name if an alias was specified 21 | * @property-read string $def The default value for this field, represented as a string 22 | * @property-read string $db 23 | * @property-read string $catalog 24 | * @property-read int $maxLength The maximum width of the field for the result set 25 | * @property-read int $length The width of the field, as specified in the table definition 26 | * @property-read int $charsetnr The character set number for the field 27 | * @property-read int $flags An integer representing the bit-flags for the field 28 | * @property-read int $type The data type used for this field 29 | * @property-read int $decimals The number of decimals used (for integer fields) 30 | * @property-read string|null $typeName The data type name 31 | * @property-read bool $flagBinary Tell if it has the MYSQLI_BINARY_FLAG bit-flag 32 | * @property-read bool $flagBlob Tell if it has the MYSQLI_BLOB_FLAG bit-flag 33 | * @property-read bool $flagEnum Tell if it has the MYSQLI_ENUM_FLAG bit-flag 34 | * @property-read bool $flagGroup Tell if it has the MYSQLI_GROUP_FLAG bit-flag 35 | * @property-read bool $flagNum Tell if it has the MYSQLI_NUM_FLAG bit-flag 36 | * @property-read bool $flagSet Tell if it has the MYSQLI_SET_FLAG bit-flag 37 | * @property-read bool $flagTimestamp Tell if it has the MYSQLI_TIMESTAMP_FLAG bit-flag 38 | * @property-read bool $flagUnsigned Tell if it has the MYSQLI_UNSIGNED_FLAG bit-flag 39 | * @property-read bool $flagZerofill Tell if it has the MYSQLI_ZEROFILL_FLAG bit-flag 40 | * @property-read bool $flagAutoIncrement Tell if it has the MYSQLI_AUTO_INCREMENT_FLAG bit-flag 41 | * @property-read bool $flagMultipleKey Tell if it has the MYSQLI_MULTIPLE_KEY_FLAG bit-flag 42 | * @property-read bool $flagNotNull Tell if it has the MYSQLI_NOT_NULL_FLAG bit-flag 43 | * @property-read bool $flagPartKey Tell if it has the MYSQLI_PART_KEY_FLAG bit-flag 44 | * @property-read bool $flagPriKey Tell if it has the MYSQLI_PRI_KEY_FLAG bit-flag 45 | * @property-read bool $flagUniqueKey Tell if it has the MYSQLI_UNIQUE_KEY_FLAG bit-flag 46 | * @property-read bool $flagNoDefaultValue Tell if it has the MYSQLI_NO_DEFAULT_VALUE_FLAG bit-flag 47 | * @property-read bool $flagOnUpdateNow Tell if it has the MYSQLI_ON_UPDATE_NOW_FLAG bit-flag 48 | * 49 | * @package database 50 | */ 51 | class Field 52 | { 53 | protected string $name; 54 | protected string $orgname; 55 | protected string $table; 56 | protected string $orgtable; 57 | protected string $def; 58 | protected string $db; 59 | protected string $catalog; 60 | protected int $maxLength; 61 | protected int $length; 62 | protected int $charsetnr; 63 | protected int $flags; 64 | protected int $type; 65 | protected int $decimals; 66 | protected ?string $typeName; 67 | protected bool $flagBinary = false; 68 | protected bool $flagBlob = false; 69 | protected bool $flagEnum = false; 70 | protected bool $flagGroup = false; 71 | protected bool $flagNum = false; 72 | protected bool $flagSet = false; 73 | protected bool $flagTimestamp = false; 74 | protected bool $flagUnsigned = false; 75 | protected bool $flagZerofill = false; 76 | protected bool $flagAutoIncrement = false; 77 | protected bool $flagMultipleKey = false; 78 | protected bool $flagNotNull = false; 79 | protected bool $flagPartKey = false; 80 | protected bool $flagPriKey = false; 81 | protected bool $flagUniqueKey = false; 82 | protected bool $flagNoDefaultValue = false; 83 | protected bool $flagOnUpdateNow = false; 84 | 85 | public function __construct(\stdClass $field) 86 | { 87 | foreach ((array) $field as $key => $value) { 88 | $key = \ucwords($key, '_'); 89 | $key = \strtr($key, ['_' => '']); 90 | $key[0] = \strtolower($key[0]); 91 | $this->{$key} = $value; 92 | } 93 | $this->setTypeName(); 94 | $this->setFlags(); 95 | } 96 | 97 | public function __get(string $name) : mixed 98 | { 99 | if (\property_exists($this, $name)) { 100 | return $this->{$name}; 101 | } 102 | throw new Error( 103 | 'Undefined property: ' . static::class . '::$' . $name 104 | ); 105 | } 106 | 107 | protected function setTypeName() : void 108 | { 109 | $this->typeName = match ($this->type) { 110 | \MYSQLI_TYPE_BIT => 'bit', 111 | \MYSQLI_TYPE_BLOB => 'blob', 112 | \MYSQLI_TYPE_CHAR => 'char', 113 | \MYSQLI_TYPE_DATE => 'date', 114 | \MYSQLI_TYPE_DATETIME => 'datetime', 115 | \MYSQLI_TYPE_DECIMAL => 'decimal', 116 | \MYSQLI_TYPE_DOUBLE => 'double', 117 | \MYSQLI_TYPE_ENUM => 'enum', 118 | \MYSQLI_TYPE_FLOAT => 'float', 119 | \MYSQLI_TYPE_GEOMETRY => 'geometry', 120 | \MYSQLI_TYPE_INT24 => 'int24', 121 | //\MYSQLI_TYPE_INTERVAL => 'interval', 122 | \MYSQLI_TYPE_JSON => 'json', 123 | \MYSQLI_TYPE_LONG => 'long', 124 | \MYSQLI_TYPE_LONG_BLOB => 'long_blob', 125 | \MYSQLI_TYPE_LONGLONG => 'longlong', 126 | \MYSQLI_TYPE_MEDIUM_BLOB => 'medium_blob', 127 | \MYSQLI_TYPE_NEWDATE => 'newdate', 128 | \MYSQLI_TYPE_NEWDECIMAL => 'newdecimal', 129 | \MYSQLI_TYPE_NULL => 'null', 130 | \MYSQLI_TYPE_SET => 'set', 131 | \MYSQLI_TYPE_SHORT => 'short', 132 | \MYSQLI_TYPE_STRING => 'string', 133 | \MYSQLI_TYPE_TIME => 'time', 134 | \MYSQLI_TYPE_TIMESTAMP => 'timestamp', 135 | //\MYSQLI_TYPE_TINY => 'tiny', 136 | \MYSQLI_TYPE_TINY_BLOB => 'tiny_blob', 137 | \MYSQLI_TYPE_VAR_STRING => 'var_string', 138 | \MYSQLI_TYPE_YEAR => 'year', 139 | default => null 140 | }; 141 | } 142 | 143 | protected function setFlags() : void 144 | { 145 | if ($this->flags & \MYSQLI_BINARY_FLAG) { 146 | $this->flagBinary = true; 147 | } 148 | if ($this->flags & \MYSQLI_BLOB_FLAG) { 149 | $this->flagBlob = true; 150 | } 151 | if ($this->flags & \MYSQLI_ENUM_FLAG) { 152 | $this->flagEnum = true; 153 | } 154 | if ($this->flags & \MYSQLI_GROUP_FLAG) { 155 | $this->flagGroup = true; 156 | } 157 | if ($this->flags & \MYSQLI_NUM_FLAG) { 158 | $this->flagNum = true; 159 | } 160 | if ($this->flags & \MYSQLI_SET_FLAG) { 161 | $this->flagSet = true; 162 | } 163 | if ($this->flags & \MYSQLI_TIMESTAMP_FLAG) { 164 | $this->flagTimestamp = true; 165 | } 166 | if ($this->flags & \MYSQLI_UNSIGNED_FLAG) { 167 | $this->flagUnsigned = true; 168 | } 169 | if ($this->flags & \MYSQLI_ZEROFILL_FLAG) { 170 | $this->flagZerofill = true; 171 | } 172 | if ($this->flags & \MYSQLI_AUTO_INCREMENT_FLAG) { 173 | $this->flagAutoIncrement = true; 174 | } 175 | if ($this->flags & \MYSQLI_MULTIPLE_KEY_FLAG) { 176 | $this->flagMultipleKey = true; 177 | } 178 | if ($this->flags & \MYSQLI_NOT_NULL_FLAG) { 179 | $this->flagNotNull = true; 180 | } 181 | if ($this->flags & \MYSQLI_PART_KEY_FLAG) { 182 | $this->flagPartKey = true; 183 | } 184 | if ($this->flags & \MYSQLI_PRI_KEY_FLAG) { 185 | $this->flagPriKey = true; 186 | } 187 | if ($this->flags & \MYSQLI_UNIQUE_KEY_FLAG) { 188 | $this->flagUniqueKey = true; 189 | } 190 | if ($this->flags & \MYSQLI_NO_DEFAULT_VALUE_FLAG) { 191 | $this->flagNoDefaultValue = true; 192 | } 193 | if ($this->flags & \MYSQLI_ON_UPDATE_NOW_FLAG) { 194 | $this->flagOnUpdateNow = true; 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/Manipulation/LoadData.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Manipulation; 11 | 12 | use InvalidArgumentException; 13 | use LogicException; 14 | 15 | /** 16 | * Class LoadData. 17 | * 18 | * @see https://mariadb.com/kb/en/load-data-infile/ 19 | * 20 | * @package database 21 | */ 22 | class LoadData extends Statement 23 | { 24 | use Traits\Set; 25 | 26 | /** 27 | * @see https://mariadb.com/kb/en/high_priority-and-low_priority/ 28 | * 29 | * @var string 30 | */ 31 | public const OPT_LOW_PRIORITY = 'LOW_PRIORITY'; 32 | /** 33 | * @see https://mariadb.com/kb/en/load-data-infile/#priority-and-concurrency 34 | * 35 | * @var string 36 | */ 37 | public const OPT_CONCURRENT = 'CONCURRENT'; 38 | /** 39 | * @see https://mariadb.com/kb/en/load-data-infile/#load-data-local-infile 40 | * 41 | * @var string 42 | */ 43 | public const OPT_LOCAL = 'LOCAL'; 44 | 45 | protected function renderOptions() : ?string 46 | { 47 | if ( ! $this->hasOptions()) { 48 | return null; 49 | } 50 | $options = $this->sql['options']; 51 | foreach ($options as &$option) { 52 | $input = $option; 53 | $option = \strtoupper($option); 54 | if ( ! \in_array($option, [ 55 | static::OPT_LOW_PRIORITY, 56 | static::OPT_CONCURRENT, 57 | static::OPT_LOCAL, 58 | ], true)) { 59 | throw new InvalidArgumentException("Invalid option: {$input}"); 60 | } 61 | } 62 | unset($option); 63 | $intersection = \array_intersect( 64 | $options, 65 | [static::OPT_LOW_PRIORITY, static::OPT_CONCURRENT] 66 | ); 67 | if (\count($intersection) > 1) { 68 | throw new LogicException('Options LOW_PRIORITY and CONCURRENT can not be used together'); 69 | } 70 | return \implode(' ', $options); 71 | } 72 | 73 | /** 74 | * @param string $filename 75 | * 76 | * @return static 77 | */ 78 | public function infile(string $filename) : static 79 | { 80 | $this->sql['infile'] = $filename; 81 | return $this; 82 | } 83 | 84 | protected function renderInfile() : string 85 | { 86 | if (empty($this->sql['infile'])) { 87 | throw new LogicException('INFILE statement is required'); 88 | } 89 | $filename = $this->database->quote($this->sql['infile']); 90 | return " INFILE {$filename}"; 91 | } 92 | 93 | /** 94 | * @param string $table 95 | * 96 | * @return static 97 | */ 98 | public function intoTable(string $table) : static 99 | { 100 | $this->sql['table'] = $table; 101 | return $this; 102 | } 103 | 104 | protected function renderIntoTable() : string 105 | { 106 | if (empty($this->sql['table'])) { 107 | throw new LogicException('Table is required'); 108 | } 109 | return ' INTO TABLE ' . $this->database->protectIdentifier($this->sql['table']); 110 | } 111 | 112 | /** 113 | * @param string $charset 114 | * 115 | * @see https://mariadb.com/kb/en/supported-character-sets-and-collations/ 116 | * 117 | * @return static 118 | */ 119 | public function charset(string $charset) : static 120 | { 121 | $this->sql['charset'] = $charset; 122 | return $this; 123 | } 124 | 125 | protected function renderCharset() : ?string 126 | { 127 | if ( ! isset($this->sql['charset'])) { 128 | return null; 129 | } 130 | return " CHARACTER SET {$this->sql['charset']}"; 131 | } 132 | 133 | /** 134 | * @param string $str 135 | * 136 | * @return static 137 | */ 138 | public function columnsTerminatedBy(string $str) : static 139 | { 140 | $this->sql['columns_terminated_by'] = $this->database->quote($str); 141 | return $this; 142 | } 143 | 144 | /** 145 | * @param string $char 146 | * @param bool $optionally 147 | * 148 | * @return static 149 | */ 150 | public function columnsEnclosedBy(string $char, bool $optionally = false) : static 151 | { 152 | $this->sql['columns_enclosed_by'] = $this->database->quote($char); 153 | $this->sql['columns_enclosed_by_opt'] = $optionally; 154 | return $this; 155 | } 156 | 157 | /** 158 | * @param string $char 159 | * 160 | * @return static 161 | */ 162 | public function columnsEscapedBy(string $char) : static 163 | { 164 | $this->sql['columns_escaped_by'] = $this->database->quote($char); 165 | return $this; 166 | } 167 | 168 | protected function renderColumns() : ?string 169 | { 170 | if ( ! isset($this->sql['columns_terminated_by']) 171 | && ! isset($this->sql['columns_enclosed_by']) 172 | && ! isset($this->sql['columns_escaped_by'])) { 173 | return null; 174 | } 175 | $part = ' COLUMNS' . \PHP_EOL; 176 | if (isset($this->sql['columns_terminated_by'])) { 177 | $part .= ' TERMINATED BY ' . $this->sql['columns_terminated_by'] . \PHP_EOL; 178 | } 179 | if (isset($this->sql['columns_enclosed_by'])) { 180 | if (isset($this->sql['columns_enclosed_by_opt'])) { 181 | $part .= ' OPTIONALLY'; 182 | } 183 | $part .= ' ENCLOSED BY ' . $this->sql['columns_enclosed_by'] . \PHP_EOL; 184 | } 185 | if (isset($this->sql['columns_escaped_by'])) { 186 | $part .= ' ESCAPED BY ' . $this->sql['columns_escaped_by'] . \PHP_EOL; 187 | } 188 | return $part; 189 | } 190 | 191 | /** 192 | * @param string $str 193 | * 194 | * @return static 195 | */ 196 | public function linesStartingBy(string $str) : static 197 | { 198 | $this->sql['lines_starting_by'] = $this->database->quote($str); 199 | return $this; 200 | } 201 | 202 | /** 203 | * @param string $str 204 | * 205 | * @return static 206 | */ 207 | public function linesTerminatedBy(string $str) : static 208 | { 209 | $this->sql['lines_terminated_by'] = $this->database->quote($str); 210 | return $this; 211 | } 212 | 213 | protected function renderLines() : ?string 214 | { 215 | if ( ! isset($this->sql['lines_starting_by']) 216 | && ! isset($this->sql['lines_terminated_by'])) { 217 | return null; 218 | } 219 | $part = ' LINES' . \PHP_EOL; 220 | if (isset($this->sql['lines_starting_by'])) { 221 | $part .= ' STARTING BY ' . $this->sql['lines_starting_by'] . \PHP_EOL; 222 | } 223 | if (isset($this->sql['lines_terminated_by'])) { 224 | $part .= ' TERMINATED BY ' . $this->sql['lines_terminated_by'] . \PHP_EOL; 225 | } 226 | return $part; 227 | } 228 | 229 | /** 230 | * @param int $number 231 | * 232 | * @return static 233 | */ 234 | public function ignoreLines(int $number) : static 235 | { 236 | $this->sql['ignore_lines'] = $number; 237 | return $this; 238 | } 239 | 240 | protected function renderIgnoreLines() : ?string 241 | { 242 | if ( ! isset($this->sql['ignore_lines'])) { 243 | return null; 244 | } 245 | return " IGNORE {$this->sql['ignore_lines']} LINES"; 246 | } 247 | 248 | /** 249 | * @return string 250 | */ 251 | public function sql() : string 252 | { 253 | $sql = 'LOAD DATA' . \PHP_EOL; 254 | $part = $this->renderOptions(); 255 | if ($part) { 256 | $sql .= $part . \PHP_EOL; 257 | } 258 | $sql .= $this->renderInfile() . \PHP_EOL; 259 | $sql .= $this->renderIntoTable() . \PHP_EOL; 260 | $part = $this->renderCharset(); 261 | if ($part) { 262 | $sql .= $part . \PHP_EOL; 263 | } 264 | $sql .= $this->renderColumns(); 265 | $sql .= $this->renderLines(); 266 | $part = $this->renderIgnoreLines(); 267 | if ($part) { 268 | $sql .= $part . \PHP_EOL; 269 | } 270 | return $sql; 271 | } 272 | 273 | public function run() : int|string 274 | { 275 | return $this->database->exec($this->sql()); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/Definition/Table/Columns/ColumnDefinition.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table\Columns; 11 | 12 | use Framework\Database\Database; 13 | use Framework\Database\Definition\Table\Columns\DateTime\DateColumn; 14 | use Framework\Database\Definition\Table\Columns\DateTime\DatetimeColumn; 15 | use Framework\Database\Definition\Table\Columns\DateTime\TimeColumn; 16 | use Framework\Database\Definition\Table\Columns\DateTime\TimestampColumn; 17 | use Framework\Database\Definition\Table\Columns\DateTime\YearColumn; 18 | use Framework\Database\Definition\Table\Columns\Geometry\GeometryCollectionColumn; 19 | use Framework\Database\Definition\Table\Columns\Geometry\GeometryColumn; 20 | use Framework\Database\Definition\Table\Columns\Geometry\LinestringColumn; 21 | use Framework\Database\Definition\Table\Columns\Geometry\MultilinestringColumn; 22 | use Framework\Database\Definition\Table\Columns\Geometry\MultipointColumn; 23 | use Framework\Database\Definition\Table\Columns\Geometry\MultipolygonColumn; 24 | use Framework\Database\Definition\Table\Columns\Geometry\PointColumn; 25 | use Framework\Database\Definition\Table\Columns\Geometry\PolygonColumn; 26 | use Framework\Database\Definition\Table\Columns\Numeric\BigintColumn; 27 | use Framework\Database\Definition\Table\Columns\Numeric\BitColumn; 28 | use Framework\Database\Definition\Table\Columns\Numeric\BooleanColumn; 29 | use Framework\Database\Definition\Table\Columns\Numeric\DecimalColumn; 30 | use Framework\Database\Definition\Table\Columns\Numeric\FloatColumn; 31 | use Framework\Database\Definition\Table\Columns\Numeric\IntColumn; 32 | use Framework\Database\Definition\Table\Columns\Numeric\MediumintColumn; 33 | use Framework\Database\Definition\Table\Columns\Numeric\SmallintColumn; 34 | use Framework\Database\Definition\Table\Columns\Numeric\TinyintColumn; 35 | use Framework\Database\Definition\Table\Columns\String\BinaryColumn; 36 | use Framework\Database\Definition\Table\Columns\String\BlobColumn; 37 | use Framework\Database\Definition\Table\Columns\String\CharColumn; 38 | use Framework\Database\Definition\Table\Columns\String\EnumColumn; 39 | use Framework\Database\Definition\Table\Columns\String\JsonColumn; 40 | use Framework\Database\Definition\Table\Columns\String\LongblobColumn; 41 | use Framework\Database\Definition\Table\Columns\String\LongtextColumn; 42 | use Framework\Database\Definition\Table\Columns\String\MediumblobColumn; 43 | use Framework\Database\Definition\Table\Columns\String\MediumtextColumn; 44 | use Framework\Database\Definition\Table\Columns\String\SetColumn; 45 | use Framework\Database\Definition\Table\Columns\String\TextColumn; 46 | use Framework\Database\Definition\Table\Columns\String\TinyblobColumn; 47 | use Framework\Database\Definition\Table\Columns\String\TinytextColumn; 48 | use Framework\Database\Definition\Table\Columns\String\VarbinaryColumn; 49 | use Framework\Database\Definition\Table\Columns\String\VarcharColumn; 50 | use Framework\Database\Definition\Table\DefinitionPart; 51 | 52 | /** 53 | * Class ColumnDefinition. 54 | * 55 | * @see https://mariadb.com/kb/en/create-table/#index-definitions 56 | * 57 | * @package database 58 | */ 59 | class ColumnDefinition extends DefinitionPart 60 | { 61 | protected Database $database; 62 | protected Column $column; 63 | 64 | public function __construct(Database $database) 65 | { 66 | $this->database = $database; 67 | } 68 | 69 | public function int(int $maximum = null) : IntColumn 70 | { 71 | return $this->column = new IntColumn($this->database, $maximum); 72 | } 73 | 74 | public function bigint(int $maximum = null) : BigintColumn 75 | { 76 | return $this->column = new BigintColumn($this->database, $maximum); 77 | } 78 | 79 | public function tinyint(int $maximum = null) : TinyintColumn 80 | { 81 | return $this->column = new TinyintColumn($this->database, $maximum); 82 | } 83 | 84 | public function decimal(int $maximum = null, int $decimals = null) : DecimalColumn 85 | { 86 | return $this->column = new DecimalColumn($this->database, $maximum, $decimals); 87 | } 88 | 89 | public function float(int $maximum = null, int $decimals = null) : FloatColumn 90 | { 91 | return $this->column = new FloatColumn($this->database, $maximum, $decimals); 92 | } 93 | 94 | public function mediumint(int $maximum = null) : MediumintColumn 95 | { 96 | return $this->column = new MediumintColumn($this->database, $maximum); 97 | } 98 | 99 | public function smallint(int $maximum = null) : SmallintColumn 100 | { 101 | return $this->column = new SmallintColumn($this->database, $maximum); 102 | } 103 | 104 | public function boolean(int $maximum = null) : BooleanColumn 105 | { 106 | return $this->column = new BooleanColumn($this->database, $maximum); 107 | } 108 | 109 | public function varchar(int $maximum = null) : VarcharColumn 110 | { 111 | return $this->column = new VarcharColumn($this->database, $maximum); 112 | } 113 | 114 | public function char(int $maximum = null) : CharColumn 115 | { 116 | return $this->column = new CharColumn($this->database, $maximum); 117 | } 118 | 119 | public function enum(string $value, string ...$values) : EnumColumn 120 | { 121 | return $this->column = new EnumColumn($this->database, $value, ...$values); 122 | } 123 | 124 | public function set(string $value, string ...$values) : SetColumn 125 | { 126 | return $this->column = new SetColumn($this->database, $value, ...$values); 127 | } 128 | 129 | public function text(int $maximum = null) : TextColumn 130 | { 131 | return $this->column = new TextColumn($this->database, $maximum); 132 | } 133 | 134 | public function longtext() : LongtextColumn 135 | { 136 | return $this->column = new LongtextColumn($this->database); 137 | } 138 | 139 | public function mediumtext() : MediumtextColumn 140 | { 141 | return $this->column = new MediumtextColumn($this->database); 142 | } 143 | 144 | public function tinytext() : TinytextColumn 145 | { 146 | return $this->column = new TinytextColumn($this->database); 147 | } 148 | 149 | public function json() : JsonColumn 150 | { 151 | return $this->column = new JsonColumn($this->database); 152 | } 153 | 154 | public function blob() : BlobColumn 155 | { 156 | return $this->column = new BlobColumn($this->database); 157 | } 158 | 159 | public function tinyblob() : TinyblobColumn 160 | { 161 | return $this->column = new TinyblobColumn($this->database); 162 | } 163 | 164 | public function mediumblob() : MediumblobColumn 165 | { 166 | return $this->column = new MediumblobColumn($this->database); 167 | } 168 | 169 | public function longblob() : LongblobColumn 170 | { 171 | return $this->column = new LongblobColumn($this->database); 172 | } 173 | 174 | public function bit() : BitColumn 175 | { 176 | return $this->column = new BitColumn($this->database); 177 | } 178 | 179 | public function binary() : BinaryColumn 180 | { 181 | return $this->column = new BinaryColumn($this->database); 182 | } 183 | 184 | public function varbinary() : VarbinaryColumn 185 | { 186 | return $this->column = new VarbinaryColumn($this->database); 187 | } 188 | 189 | public function date() : DateColumn 190 | { 191 | return $this->column = new DateColumn($this->database); 192 | } 193 | 194 | public function time() : TimeColumn 195 | { 196 | return $this->column = new TimeColumn($this->database); 197 | } 198 | 199 | public function datetime() : DatetimeColumn 200 | { 201 | return $this->column = new DatetimeColumn($this->database); 202 | } 203 | 204 | public function timestamp() : TimestampColumn 205 | { 206 | return $this->column = new TimestampColumn($this->database); 207 | } 208 | 209 | public function year() : YearColumn 210 | { 211 | return $this->column = new YearColumn($this->database); 212 | } 213 | 214 | public function geometrycollection() : GeometryCollectionColumn 215 | { 216 | return $this->column = new GeometryCollectionColumn($this->database); 217 | } 218 | 219 | public function geometry() : GeometryColumn 220 | { 221 | return $this->column = new GeometryColumn($this->database); 222 | } 223 | 224 | public function linestring() : LinestringColumn 225 | { 226 | return $this->column = new LinestringColumn($this->database); 227 | } 228 | 229 | public function multilinestring() : MultilinestringColumn 230 | { 231 | return $this->column = new MultilinestringColumn($this->database); 232 | } 233 | 234 | public function multipoint() : MultipointColumn 235 | { 236 | return $this->column = new MultipointColumn($this->database); 237 | } 238 | 239 | public function multipolygon() : MultipolygonColumn 240 | { 241 | return $this->column = new MultipolygonColumn($this->database); 242 | } 243 | 244 | public function point() : PointColumn 245 | { 246 | return $this->column = new PointColumn($this->database); 247 | } 248 | 249 | public function polygon() : PolygonColumn 250 | { 251 | return $this->column = new PolygonColumn($this->database); 252 | } 253 | 254 | protected function sql() : string 255 | { 256 | return $this->column->sql(); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /.phpstorm.meta.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace PHPSTORM_META; 11 | 12 | registerArgumentsSet( 13 | 'charsets', 14 | 'armscii8', 15 | 'ascii', 16 | 'big5', 17 | 'binary', 18 | 'cp1250', 19 | 'cp1251', 20 | 'cp1256', 21 | 'cp1257', 22 | 'cp850', 23 | 'cp852', 24 | 'cp866', 25 | 'cp932', 26 | 'dec8', 27 | 'eucjpms', 28 | 'euckr', 29 | 'gb2312', 30 | 'gbk', 31 | 'geostd8', 32 | 'greek', 33 | 'hebrew', 34 | 'hp8', 35 | 'keybcs2', 36 | 'koi8r', 37 | 'koi8u', 38 | 'latin1', 39 | 'latin2', 40 | 'latin5', 41 | 'latin7', 42 | 'macce', 43 | 'macroman', 44 | 'sjis', 45 | 'swe7', 46 | 'tis620', 47 | 'ucs2', 48 | 'ujis', 49 | 'utf16', 50 | 'utf16le', 51 | 'utf32', 52 | 'utf8', 53 | 'utf8mb3', 54 | 'utf8mb4', 55 | ); 56 | expectedArguments( 57 | \Framework\Database\Database::setCollations(), 58 | 0, 59 | argumentsSet('charsets') 60 | ); 61 | expectedArguments( 62 | \Framework\Database\Definition\AlterSchema::charset(), 63 | 0, 64 | argumentsSet('charsets') 65 | ); 66 | expectedArguments( 67 | \Framework\Database\Definition\AlterTable::charset(), 68 | 0, 69 | argumentsSet('charsets') 70 | ); 71 | expectedArguments( 72 | \Framework\Database\Definition\AlterTable::convertToCharset(), 73 | 0, 74 | argumentsSet('charsets') 75 | ); 76 | expectedArguments( 77 | \Framework\Database\Definition\CreateSchema::charset(), 78 | 0, 79 | argumentsSet('charsets') 80 | ); 81 | expectedArguments( 82 | \Framework\Database\Definition\Table\Columns\String\StringDataType::charset(), 83 | 0, 84 | argumentsSet('charsets') 85 | ); 86 | expectedArguments( 87 | \Framework\Database\Manipulation\LoadData::charset(), 88 | 0, 89 | argumentsSet('charsets') 90 | ); 91 | expectedArguments( 92 | \Framework\Database\Manipulation\Select::intoOutfile(), 93 | 1, 94 | argumentsSet('charsets') 95 | ); 96 | registerArgumentsSet( 97 | 'definition_alter_table_algo', 98 | \Framework\Database\Definition\AlterTable::ALGO_COPY, 99 | \Framework\Database\Definition\AlterTable::ALGO_DEFAULT, 100 | \Framework\Database\Definition\AlterTable::ALGO_INPLACE, 101 | \Framework\Database\Definition\AlterTable::ALGO_INSTANT, 102 | \Framework\Database\Definition\AlterTable::ALGO_NOCOPY, 103 | 'COPY', 104 | 'DEFAULT', 105 | 'INPLACE', 106 | 'INSTANT', 107 | 'NOCOPY', 108 | ); 109 | expectedArguments( 110 | \Framework\Database\Definition\AlterTable::algorithm(), 111 | 0, 112 | argumentsSet('definition_alter_table_algo') 113 | ); 114 | registerArgumentsSet( 115 | 'definition_alter_table_lock', 116 | \Framework\Database\Definition\AlterTable::LOCK_DEFAULT, 117 | \Framework\Database\Definition\AlterTable::LOCK_EXCLUSIVE, 118 | \Framework\Database\Definition\AlterTable::LOCK_NONE, 119 | \Framework\Database\Definition\AlterTable::LOCK_SHARED, 120 | 'DEFAULT', 121 | 'EXCLUSIVE', 122 | 'NONE', 123 | 'SHARED', 124 | ); 125 | expectedArguments( 126 | \Framework\Database\Definition\AlterTable::lock(), 127 | 0, 128 | argumentsSet('definition_alter_table_lock') 129 | ); 130 | registerArgumentsSet( 131 | 'manipulation_delete_opt', 132 | \Framework\Database\Manipulation\Delete::OPT_IGNORE, 133 | \Framework\Database\Manipulation\Delete::OPT_LOW_PRIORITY, 134 | \Framework\Database\Manipulation\Delete::OPT_QUICK, 135 | 'IGNORE', 136 | 'LOW_PRIORITY', 137 | 'QUICK', 138 | ); 139 | expectedArguments( 140 | \Framework\Database\Manipulation\Delete::options(), 141 | 0, 142 | argumentsSet('manipulation_delete_opt') 143 | ); 144 | expectedArguments( 145 | \Framework\Database\Manipulation\Delete::options(), 146 | 1, 147 | argumentsSet('manipulation_delete_opt') 148 | ); 149 | expectedArguments( 150 | \Framework\Database\Manipulation\Delete::options(), 151 | 2, 152 | argumentsSet('manipulation_delete_opt') 153 | ); 154 | registerArgumentsSet( 155 | 'manipulation_insert_opt', 156 | \Framework\Database\Manipulation\Insert::OPT_DELAYED, 157 | \Framework\Database\Manipulation\Insert::OPT_HIGH_PRIORITY, 158 | \Framework\Database\Manipulation\Insert::OPT_IGNORE, 159 | \Framework\Database\Manipulation\Insert::OPT_LOW_PRIORITY, 160 | 'DELAYED', 161 | 'HIGH_PRIORITY', 162 | 'IGNORE', 163 | 'LOW_PRIORITY', 164 | ); 165 | expectedArguments( 166 | \Framework\Database\Manipulation\Insert::options(), 167 | 0, 168 | argumentsSet('manipulation_insert_opt') 169 | ); 170 | expectedArguments( 171 | \Framework\Database\Manipulation\Insert::options(), 172 | 1, 173 | argumentsSet('manipulation_insert_opt') 174 | ); 175 | expectedArguments( 176 | \Framework\Database\Manipulation\Insert::options(), 177 | 2, 178 | argumentsSet('manipulation_insert_opt') 179 | ); 180 | registerArgumentsSet( 181 | 'manipulation_load_data_opt', 182 | \Framework\Database\Manipulation\LoadData::OPT_CONCURRENT, 183 | \Framework\Database\Manipulation\LoadData::OPT_LOCAL, 184 | \Framework\Database\Manipulation\LoadData::OPT_LOW_PRIORITY, 185 | 'CONCURRENT', 186 | 'LOCAL', 187 | 'LOW_PRIORITY', 188 | ); 189 | expectedArguments( 190 | \Framework\Database\Manipulation\LoadData::options(), 191 | 0, 192 | argumentsSet('manipulation_load_data_opt') 193 | ); 194 | expectedArguments( 195 | \Framework\Database\Manipulation\LoadData::options(), 196 | 1, 197 | argumentsSet('manipulation_load_data_opt') 198 | ); 199 | expectedArguments( 200 | \Framework\Database\Manipulation\LoadData::options(), 201 | 2, 202 | argumentsSet('manipulation_load_data_opt') 203 | ); 204 | registerArgumentsSet( 205 | 'manipulation_replace_opt', 206 | \Framework\Database\Manipulation\Replace::OPT_DELAYED, 207 | \Framework\Database\Manipulation\Replace::OPT_LOW_PRIORITY, 208 | 'DELAYED', 209 | 'LOW_PRIORITY', 210 | ); 211 | expectedArguments( 212 | \Framework\Database\Manipulation\Replace::options(), 213 | 0, 214 | argumentsSet('manipulation_replace_opt') 215 | ); 216 | expectedArguments( 217 | \Framework\Database\Manipulation\Replace::options(), 218 | 1, 219 | argumentsSet('manipulation_replace_opt') 220 | ); 221 | registerArgumentsSet( 222 | 'manipulation_select_opt', 223 | \Framework\Database\Manipulation\Select::OPT_ALL, 224 | \Framework\Database\Manipulation\Select::OPT_DISTINCT, 225 | \Framework\Database\Manipulation\Select::OPT_DISTINCTROW, 226 | \Framework\Database\Manipulation\Select::OPT_HIGH_PRIORITY, 227 | \Framework\Database\Manipulation\Select::OPT_SQL_BIG_RESULT, 228 | \Framework\Database\Manipulation\Select::OPT_SQL_BUFFER_RESULT, 229 | \Framework\Database\Manipulation\Select::OPT_SQL_CACHE, 230 | \Framework\Database\Manipulation\Select::OPT_SQL_CALC_FOUND_ROWS, 231 | \Framework\Database\Manipulation\Select::OPT_SQL_NO_CACHE, 232 | \Framework\Database\Manipulation\Select::OPT_SQL_SMALL_RESULT, 233 | \Framework\Database\Manipulation\Select::OPT_STRAIGHT_JOIN, 234 | 'ALL', 235 | 'DISTINCT', 236 | 'DISTINCTROW', 237 | 'HIGH_PRIORITY', 238 | 'SQL_BIG_RESULT', 239 | 'SQL_BUFFER_RESULT', 240 | 'SQL_CACHE', 241 | 'SQL_CALC_FOUND_ROWS', 242 | 'SQL_NO_CACHE', 243 | 'SQL_SMALL_RESULT', 244 | 'STRAIGHT_JOIN', 245 | ); 246 | expectedArguments( 247 | \Framework\Database\Manipulation\Select::options(), 248 | 0, 249 | argumentsSet('manipulation_select_opt') 250 | ); 251 | expectedArguments( 252 | \Framework\Database\Manipulation\Select::options(), 253 | 1, 254 | argumentsSet('manipulation_select_opt') 255 | ); 256 | expectedArguments( 257 | \Framework\Database\Manipulation\Select::options(), 258 | 2, 259 | argumentsSet('manipulation_select_opt') 260 | ); 261 | registerArgumentsSet( 262 | 'manipulation_update_opt', 263 | \Framework\Database\Manipulation\Update::OPT_IGNORE, 264 | \Framework\Database\Manipulation\Update::OPT_LOW_PRIORITY, 265 | 'IGNORE', 266 | 'LOW_PRIORITY', 267 | ); 268 | expectedArguments( 269 | \Framework\Database\Manipulation\Update::options(), 270 | 0, 271 | argumentsSet('manipulation_update_opt') 272 | ); 273 | expectedArguments( 274 | \Framework\Database\Manipulation\Update::options(), 275 | 1, 276 | argumentsSet('manipulation_update_opt') 277 | ); 278 | registerArgumentsSet( 279 | 'manipulation_with_opt', 280 | \Framework\Database\Manipulation\With::OPT_RECURSIVE, 281 | 'RECURSIVE', 282 | ); 283 | expectedArguments( 284 | \Framework\Database\Manipulation\With::options(), 285 | 0, 286 | argumentsSet('manipulation_with_opt') 287 | ); 288 | registerArgumentsSet( 289 | 'foreign_key_opt', 290 | \Framework\Database\Definition\Table\Indexes\Keys\ForeignKey::OPT_CASCADE, 291 | \Framework\Database\Definition\Table\Indexes\Keys\ForeignKey::OPT_NO_ACTION, 292 | \Framework\Database\Definition\Table\Indexes\Keys\ForeignKey::OPT_RESTRICT, 293 | \Framework\Database\Definition\Table\Indexes\Keys\ForeignKey::OPT_SET_NULL, 294 | 'CASCADE', 295 | 'NO ACTION', 296 | 'RESTRICT', 297 | 'SET NULL', 298 | ); 299 | expectedArguments( 300 | \Framework\Database\Definition\Table\Indexes\Keys\ForeignKey::onDelete(), 301 | 0, 302 | argumentsSet('foreign_key_opt') 303 | ); 304 | expectedArguments( 305 | \Framework\Database\Definition\Table\Indexes\Keys\ForeignKey::onUpdate(), 306 | 0, 307 | argumentsSet('foreign_key_opt') 308 | ); 309 | registerArgumentsSet( 310 | 'table_options', 311 | \Framework\Database\Definition\Table\TableStatement::OPT_AUTO_INCREMENT, 312 | \Framework\Database\Definition\Table\TableStatement::OPT_AVG_ROW_LENGTH, 313 | \Framework\Database\Definition\Table\TableStatement::OPT_CHARSET, 314 | \Framework\Database\Definition\Table\TableStatement::OPT_CHECKSUM, 315 | \Framework\Database\Definition\Table\TableStatement::OPT_COLLATE, 316 | \Framework\Database\Definition\Table\TableStatement::OPT_COMMENT, 317 | \Framework\Database\Definition\Table\TableStatement::OPT_CONNECTION, 318 | \Framework\Database\Definition\Table\TableStatement::OPT_DATA_DIRECTORY, 319 | \Framework\Database\Definition\Table\TableStatement::OPT_DELAY_KEY_WRITE, 320 | \Framework\Database\Definition\Table\TableStatement::OPT_ENCRYPTED, 321 | \Framework\Database\Definition\Table\TableStatement::OPT_ENCRYPTION_KEY_ID, 322 | \Framework\Database\Definition\Table\TableStatement::OPT_ENGINE, 323 | \Framework\Database\Definition\Table\TableStatement::OPT_IETF_QUOTES, 324 | \Framework\Database\Definition\Table\TableStatement::OPT_INDEX_DIRECTORY, 325 | \Framework\Database\Definition\Table\TableStatement::OPT_INSERT_METHOD, 326 | \Framework\Database\Definition\Table\TableStatement::OPT_KEY_BLOCK_SIZE, 327 | \Framework\Database\Definition\Table\TableStatement::OPT_MAX_ROWS, 328 | \Framework\Database\Definition\Table\TableStatement::OPT_MIN_ROWS, 329 | \Framework\Database\Definition\Table\TableStatement::OPT_PACK_KEYS, 330 | \Framework\Database\Definition\Table\TableStatement::OPT_PAGE_CHECKSUM, 331 | \Framework\Database\Definition\Table\TableStatement::OPT_PAGE_COMPRESSED, 332 | \Framework\Database\Definition\Table\TableStatement::OPT_PAGE_COMPRESSION_LEVEL, 333 | \Framework\Database\Definition\Table\TableStatement::OPT_PASSWORD, 334 | \Framework\Database\Definition\Table\TableStatement::OPT_ROW_FORMAT, 335 | \Framework\Database\Definition\Table\TableStatement::OPT_SEQUENCE, 336 | \Framework\Database\Definition\Table\TableStatement::OPT_STATS_AUTO_RECALC, 337 | \Framework\Database\Definition\Table\TableStatement::OPT_STATS_PERSISTENT, 338 | \Framework\Database\Definition\Table\TableStatement::OPT_STATS_SAMPLE_PAGES, 339 | \Framework\Database\Definition\Table\TableStatement::OPT_TABLESPACE, 340 | \Framework\Database\Definition\Table\TableStatement::OPT_TRANSACTIONAL, 341 | \Framework\Database\Definition\Table\TableStatement::OPT_UNION, 342 | \Framework\Database\Definition\Table\TableStatement::OPT_WITH_SYSTEM_VERSIONING, 343 | 'AUTO_INCREMENT', 344 | 'AVG_ROW_LENGTH', 345 | 'CHARSET', 346 | 'CHECKSUM', 347 | 'COLLATE', 348 | 'COMMENT', 349 | 'CONNECTION', 350 | 'DATA DIRECTORY', 351 | 'DELAY_KEY_WRITE', 352 | 'ENCRYPTED', 353 | 'ENCRYPTION_KEY_ID', 354 | 'ENGINE', 355 | 'IETF_QUOTES', 356 | 'INDEX DIRECTORY', 357 | 'INSERT_METHOD', 358 | 'KEY_BLOCK_SIZE', 359 | 'MAX_ROWS', 360 | 'MIN_ROWS', 361 | 'PACK_KEYS', 362 | 'PAGE_CHECKSUM', 363 | 'PAGE_COMPRESSED', 364 | 'PAGE_COMPRESSION_LEVEL', 365 | 'PASSWORD', 366 | 'ROW_FORMAT', 367 | 'SEQUENCE', 368 | 'STATS_AUTO_RECALC', 369 | 'STATS_PERSISTENT', 370 | 'STATS_SAMPLE_PAGES', 371 | 'TABLESPACE', 372 | 'TRANSACTIONAL', 373 | 'UNION', 374 | 'WITH SYSTEM VERSIONING', 375 | ); 376 | expectedArguments( 377 | \Framework\Database\Definition\Table\TableStatement::option(), 378 | 0, 379 | argumentsSet('table_options') 380 | ); 381 | registerArgumentsSet( 382 | 'operators', 383 | '=', 384 | '<=>', 385 | '!=', 386 | '<>', 387 | '>', 388 | '>=', 389 | '<', 390 | '<=', 391 | 'LIKE', 392 | 'NOT LIKE', 393 | 'IN', 394 | 'NOT IN', 395 | 'BETWEEN', 396 | 'NOT BETWEEN', 397 | 'IS NULL', 398 | 'IS NOT NULL', 399 | ); 400 | expectedArguments( 401 | \Framework\Database\Manipulation\Traits\Having::having(), 402 | 1, 403 | argumentsSet('operators') 404 | ); 405 | expectedArguments( 406 | \Framework\Database\Manipulation\Traits\Having::orHaving(), 407 | 1, 408 | argumentsSet('operators') 409 | ); 410 | expectedArguments( 411 | \Framework\Database\Manipulation\Traits\Where::where(), 412 | 1, 413 | argumentsSet('operators') 414 | ); 415 | expectedArguments( 416 | \Framework\Database\Manipulation\Traits\Where::orWhere(), 417 | 1, 418 | argumentsSet('operators') 419 | ); 420 | -------------------------------------------------------------------------------- /src/Definition/Table/TableStatement.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Framework\Database\Definition\Table; 11 | 12 | use Framework\Database\Statement; 13 | use InvalidArgumentException; 14 | 15 | /** 16 | * Class TableStatement. 17 | * 18 | * @see https://mariadb.com/kb/en/create-table/#table-options 19 | * 20 | * @package database 21 | */ 22 | abstract class TableStatement extends Statement 23 | { 24 | /** 25 | * @see https://mariadb.com/kb/en/create-table/#storage-engine 26 | * 27 | * @var string 28 | */ 29 | public const OPT_ENGINE = 'ENGINE'; 30 | /** 31 | * @see https://mariadb.com/kb/en/create-table/#auto_increment 32 | * 33 | * @var string 34 | */ 35 | public const OPT_AUTO_INCREMENT = 'AUTO_INCREMENT'; 36 | /** 37 | * @see https://mariadb.com/kb/en/create-table/#avg_row_length 38 | * 39 | * @var string 40 | */ 41 | public const OPT_AVG_ROW_LENGTH = 'AVG_ROW_LENGTH'; 42 | /** 43 | * @see https://mariadb.com/kb/en/create-table/#default-character-setcharset 44 | * 45 | * @var string 46 | */ 47 | public const OPT_CHARSET = 'CHARSET'; 48 | /** 49 | * @see https://mariadb.com/kb/en/create-table/#checksumtable_checksum 50 | * 51 | * @var string 52 | */ 53 | public const OPT_CHECKSUM = 'CHECKSUM'; 54 | /** 55 | * @see https://mariadb.com/kb/en/create-table/#default-collate 56 | * 57 | * @var string 58 | */ 59 | public const OPT_COLLATE = 'COLLATE'; 60 | /** 61 | * @see https://mariadb.com/kb/en/create-table/#comment 62 | * 63 | * @var string 64 | */ 65 | public const OPT_COMMENT = 'COMMENT'; 66 | /** 67 | * @see https://mariadb.com/kb/en/create-table/#connection 68 | * 69 | * @var string 70 | */ 71 | public const OPT_CONNECTION = 'CONNECTION'; 72 | /** 73 | * @see https://mariadb.com/kb/en/create-table/#data-directoryindex-directory 74 | * 75 | * @var string 76 | */ 77 | public const OPT_DATA_DIRECTORY = 'DATA DIRECTORY'; 78 | /** 79 | * @see https://mariadb.com/kb/en/create-table/#delay_key_write 80 | * 81 | * @var string 82 | */ 83 | public const OPT_DELAY_KEY_WRITE = 'DELAY_KEY_WRITE'; 84 | /** 85 | * @see https://mariadb.com/kb/en/create-table/#encrypted 86 | * 87 | * @var string 88 | */ 89 | public const OPT_ENCRYPTED = 'ENCRYPTED'; 90 | /** 91 | * @see https://mariadb.com/kb/en/create-table/#encryption_key_id 92 | * 93 | * @var string 94 | */ 95 | public const OPT_ENCRYPTION_KEY_ID = 'ENCRYPTION_KEY_ID'; 96 | /** 97 | * @see https://mariadb.com/kb/en/create-table/#ietf_quotes 98 | * 99 | * @var string 100 | */ 101 | public const OPT_IETF_QUOTES = 'IETF_QUOTES'; 102 | /** 103 | * @see https://mariadb.com/kb/en/create-table/#data-directoryindex-directory 104 | * 105 | * @var string 106 | */ 107 | public const OPT_INDEX_DIRECTORY = 'INDEX DIRECTORY'; 108 | /** 109 | * @see https://mariadb.com/kb/en/create-table/#insert_method 110 | * 111 | * @var string 112 | */ 113 | public const OPT_INSERT_METHOD = 'INSERT_METHOD'; 114 | /** 115 | * @see https://mariadb.com/kb/en/create-table/#key_block_size 116 | * 117 | * @var string 118 | */ 119 | public const OPT_KEY_BLOCK_SIZE = 'KEY_BLOCK_SIZE'; 120 | /** 121 | * @see https://mariadb.com/kb/en/create-table/#min_rowsmax_rows 122 | * 123 | * @var string 124 | */ 125 | public const OPT_MAX_ROWS = 'MAX_ROWS'; 126 | /** 127 | * @see https://mariadb.com/kb/en/create-table/#min_rowsmax_rows 128 | * 129 | * @var string 130 | */ 131 | public const OPT_MIN_ROWS = 'MIN_ROWS'; 132 | /** 133 | * @see https://mariadb.com/kb/en/create-table/#pack_keys 134 | * 135 | * @var string 136 | */ 137 | public const OPT_PACK_KEYS = 'PACK_KEYS'; 138 | /** 139 | * @see https://mariadb.com/kb/en/create-table/#page_checksum 140 | * 141 | * @var string 142 | */ 143 | public const OPT_PAGE_CHECKSUM = 'PAGE_CHECKSUM'; 144 | /** 145 | * @see https://mariadb.com/kb/en/create-table/#page_compressed 146 | * 147 | * @var string 148 | */ 149 | public const OPT_PAGE_COMPRESSED = 'PAGE_COMPRESSED'; 150 | /** 151 | * @see https://mariadb.com/kb/en/create-table/#page_compression_level 152 | * 153 | * @var string 154 | */ 155 | public const OPT_PAGE_COMPRESSION_LEVEL = 'PAGE_COMPRESSION_LEVEL'; 156 | /** 157 | * @see https://mariadb.com/kb/en/create-table/#password 158 | * 159 | * @var string 160 | */ 161 | public const OPT_PASSWORD = 'PASSWORD'; 162 | /** 163 | * @see https://mariadb.com/kb/en/create-table/#row_format 164 | * 165 | * @var string 166 | */ 167 | public const OPT_ROW_FORMAT = 'ROW_FORMAT'; 168 | /** 169 | * @see https://mariadb.com/kb/en/create-table/#sequence 170 | * 171 | * @var string 172 | */ 173 | public const OPT_SEQUENCE = 'SEQUENCE'; 174 | /** 175 | * @see https://mariadb.com/kb/en/create-table/#stats_auto_recalc 176 | * 177 | * @var string 178 | */ 179 | public const OPT_STATS_AUTO_RECALC = 'STATS_AUTO_RECALC'; 180 | /** 181 | * @see https://mariadb.com/kb/en/create-table/#stats_persistent 182 | * 183 | * @var string 184 | */ 185 | public const OPT_STATS_PERSISTENT = 'STATS_PERSISTENT'; 186 | /** 187 | * @see https://mariadb.com/kb/en/create-table/#stats_sample_pages 188 | * 189 | * @var string 190 | */ 191 | public const OPT_STATS_SAMPLE_PAGES = 'STATS_SAMPLE_PAGES'; 192 | /** 193 | * @see https://mariadb.com/kb/en/create-tablespace/ 194 | * 195 | * @var string 196 | */ 197 | public const OPT_TABLESPACE = 'TABLESPACE'; 198 | /** 199 | * @see https://mariadb.com/kb/en/create-table/#transactional 200 | * 201 | * @var string 202 | */ 203 | public const OPT_TRANSACTIONAL = 'TRANSACTIONAL'; 204 | /** 205 | * @see https://mariadb.com/kb/en/create-table/#union 206 | * 207 | * @var string 208 | */ 209 | public const OPT_UNION = 'UNION'; 210 | /** 211 | * @see https://mariadb.com/kb/en/create-table/#with-system-versioning 212 | * 213 | * @var string 214 | */ 215 | public const OPT_WITH_SYSTEM_VERSIONING = 'WITH SYSTEM VERSIONING'; 216 | 217 | /** 218 | * Adds a table option. 219 | * 220 | * @param string $name 221 | * @param int|string|null $value 222 | * 223 | * @return static 224 | */ 225 | public function option(string $name, int | string $value = null) : static 226 | { 227 | $this->sql['options'][$name] = $value; 228 | return $this; 229 | } 230 | 231 | /** 232 | * Adds table options. 233 | * 234 | * @param array $options 235 | * 236 | * @return static 237 | */ 238 | public function options(array $options) : static 239 | { 240 | foreach ($options as $name => $value) { 241 | $this->option($name, $value); 242 | } 243 | return $this; 244 | } 245 | 246 | protected function renderOptions() : ?string 247 | { 248 | if ( ! isset($this->sql['options'])) { 249 | return null; 250 | } 251 | $options = []; 252 | foreach ($this->sql['options'] as $name => $value) { 253 | $nameUpper = \strtoupper($name); 254 | $value = (string) $value; 255 | $value = match ($nameUpper) { 256 | static::OPT_ENGINE => $this->makeEngine($value), 257 | static::OPT_AUTO_INCREMENT => $this->makeAutoIncrement($value), 258 | static::OPT_AVG_ROW_LENGTH => $this->makeAvgRowLength($value), 259 | static::OPT_CHARSET => $this->makeCharset($value), 260 | static::OPT_CHECKSUM => $this->makeChecksum($value), 261 | static::OPT_COLLATE => $this->makeCollate($value), 262 | static::OPT_COMMENT => $this->makeComment($value), 263 | static::OPT_CONNECTION => $this->makeConnection($value), 264 | static::OPT_DATA_DIRECTORY => $this->makeDataDirectory($value), 265 | static::OPT_DELAY_KEY_WRITE => $this->makeDelayKeyWrite($value), 266 | static::OPT_ENCRYPTED => $this->makeEncrypted($value), 267 | static::OPT_ENCRYPTION_KEY_ID => $this->makeEncryptionKeyId($value), 268 | static::OPT_IETF_QUOTES => $this->makeIetfQuotes($value), 269 | static::OPT_INDEX_DIRECTORY => $this->makeIndexDirectory($value), 270 | static::OPT_INSERT_METHOD => $this->makeInsertMethod($value), 271 | static::OPT_KEY_BLOCK_SIZE => $this->makeKeyBlockSize($value), 272 | static::OPT_MAX_ROWS => $this->makeMaxRows($value), 273 | static::OPT_MIN_ROWS => $this->makeMinRows($value), 274 | static::OPT_PACK_KEYS => $this->makePackKeys($value), 275 | static::OPT_PAGE_CHECKSUM => $this->makePageChecksum($value), 276 | static::OPT_PAGE_COMPRESSED => $this->makePageCompressed($value), 277 | static::OPT_PAGE_COMPRESSION_LEVEL => $this->makePageCompressionLevel($value), 278 | static::OPT_PASSWORD => $this->makePassword($value), 279 | static::OPT_ROW_FORMAT => $this->makeRowFormat($value), 280 | static::OPT_SEQUENCE => $this->makeSequence($value), 281 | static::OPT_STATS_AUTO_RECALC => $this->makeStatsAutoRecalc($value), 282 | static::OPT_STATS_PERSISTENT => $this->makeStatsPersistent($value), 283 | static::OPT_STATS_SAMPLE_PAGES => $this->makeStatsSamplePages($value), 284 | static::OPT_TABLESPACE => $this->makeTablespace($value), 285 | static::OPT_TRANSACTIONAL => $this->makeTransactional($value), 286 | static::OPT_UNION => $this->makeUnion($value), 287 | static::OPT_WITH_SYSTEM_VERSIONING => '', 288 | default => throw new InvalidArgumentException('Invalid option: ' . $name) 289 | }; 290 | $option = $nameUpper; 291 | if ($value !== '') { 292 | $option .= ' = ' . $value; 293 | } 294 | $options[] = $option; 295 | } 296 | return ' ' . \implode(', ', $options); 297 | } 298 | 299 | private function makeEngine(string $value) : string 300 | { 301 | return $this->getValue(static::OPT_ENGINE, $value, [ 302 | 'aria' => 'Aria', 303 | 'csv' => 'CSV', 304 | 'innodb' => 'InnoDB', 305 | 'memory' => 'MEMORY', 306 | 'mrg_myisam' => 'MRG_MyISAM', 307 | 'myisam' => 'MyISAM', 308 | 'sequence' => 'SEQUENCE', 309 | ], \strtolower($value), true); 310 | } 311 | 312 | private function makeAutoIncrement(string $value) : string 313 | { 314 | return \is_numeric($value) 315 | ? $value 316 | : throw $this->invalidValue(static::OPT_AUTO_INCREMENT, $value); 317 | } 318 | 319 | private function makeAvgRowLength(string $value) : string 320 | { 321 | if (\is_numeric($value) && $value >= 0) { 322 | return $value; 323 | } 324 | throw $this->invalidValue(static::OPT_AVG_ROW_LENGTH, $value); 325 | } 326 | 327 | private function makeCharset(string $value) : string 328 | { 329 | return $this->getValue(static::OPT_CHARSET, $value, [ 330 | 'armscii8', 331 | 'ascii', 332 | 'big5', 333 | 'binary', 334 | 'cp1250', 335 | 'cp1251', 336 | 'cp1256', 337 | 'cp1257', 338 | 'cp850', 339 | 'cp852', 340 | 'cp866', 341 | 'cp932', 342 | 'dec8', 343 | 'eucjpms', 344 | 'euckr', 345 | 'gb2312', 346 | 'gbk', 347 | 'geostd8', 348 | 'greek', 349 | 'hebrew', 350 | 'hp8', 351 | 'keybcs2', 352 | 'koi8r', 353 | 'koi8u', 354 | 'latin1', 355 | 'latin2', 356 | 'latin5', 357 | 'latin7', 358 | 'macce', 359 | 'macroman', 360 | 'sjis', 361 | 'swe7', 362 | 'tis620', 363 | 'ucs2', 364 | 'ujis', 365 | 'utf16', 366 | 'utf16le', 367 | 'utf32', 368 | 'utf8', 369 | 'utf8mb3', 370 | 'utf8mb4', 371 | ], \strtolower($value)); 372 | } 373 | 374 | private function makeChecksum(string $value) : string 375 | { 376 | return $this->getValue(static::OPT_CHECKSUM, $value, [ 377 | '0', 378 | '1', 379 | ]); 380 | } 381 | 382 | private function makeCollate(string $value) : string 383 | { 384 | return $this->quote($value); 385 | } 386 | 387 | private function makeComment(string $value) : string 388 | { 389 | return $this->quote($value); 390 | } 391 | 392 | private function makeConnection(string $value) : string 393 | { 394 | return $this->quote($value); 395 | } 396 | 397 | private function makeDataDirectory(string $value) : string 398 | { 399 | return $this->quote($value); 400 | } 401 | 402 | private function makeDelayKeyWrite(string $value) : string 403 | { 404 | return $this->getValue(static::OPT_DELAY_KEY_WRITE, $value, [ 405 | '0', 406 | '1', 407 | ]); 408 | } 409 | 410 | private function makeIetfQuotes(string $value) : string 411 | { 412 | return $this->getValue(static::OPT_IETF_QUOTES, $value, [ 413 | 'NO', 414 | 'YES', 415 | ], \strtoupper($value)); 416 | } 417 | 418 | private function makeIndexDirectory(string $value) : string 419 | { 420 | return $this->quote($value); 421 | } 422 | 423 | private function makeInsertMethod(string $value) : string 424 | { 425 | return $this->getValue(static::OPT_INSERT_METHOD, $value, [ 426 | 'FIRST', 427 | 'LAST', 428 | 'NO', 429 | ], \strtoupper($value)); 430 | } 431 | 432 | private function makeKeyBlockSize(string $value) : string 433 | { 434 | if (\is_numeric($value) && $value >= 0) { 435 | return $value; 436 | } 437 | throw $this->invalidValue(static::OPT_KEY_BLOCK_SIZE, $value); 438 | } 439 | 440 | private function makeEncrypted(string $value) : string 441 | { 442 | return $this->getValue(static::OPT_ENCRYPTED, $value, [ 443 | 'NO', 444 | 'YES', 445 | ], \strtoupper($value)); 446 | } 447 | 448 | private function makeEncryptionKeyId(string $value) : string 449 | { 450 | if (\is_numeric($value) && $value >= 1) { 451 | return $value; 452 | } 453 | throw $this->invalidValue(static::OPT_ENCRYPTION_KEY_ID, $value); 454 | } 455 | 456 | private function makeMaxRows(string $value) : string 457 | { 458 | if (\is_numeric($value) && $value >= 0) { 459 | return $value; 460 | } 461 | throw $this->invalidValue(static::OPT_MAX_ROWS, $value); 462 | } 463 | 464 | private function makeMinRows(string $value) : string 465 | { 466 | if (\is_numeric($value) && $value >= 0) { 467 | return $value; 468 | } 469 | throw $this->invalidValue(static::OPT_MIN_ROWS, $value); 470 | } 471 | 472 | private function makePackKeys(string $value) : string 473 | { 474 | return $this->getValue(static::OPT_PACK_KEYS, $value, [ 475 | '0', 476 | '1', 477 | ]); 478 | } 479 | 480 | private function makePageChecksum(string $value) : string 481 | { 482 | return $this->getValue(static::OPT_PAGE_CHECKSUM, $value, [ 483 | '0', 484 | '1', 485 | ]); 486 | } 487 | 488 | private function makePageCompressed(string $value) : string 489 | { 490 | return $this->getValue( 491 | static::OPT_PAGE_COMPRESSED, 492 | $value, 493 | \array_map('strval', \range(0, 9)) 494 | ); 495 | } 496 | 497 | private function makePageCompressionLevel(string $value) : string 498 | { 499 | return $this->getValue(static::OPT_PAGE_COMPRESSION_LEVEL, $value, [ 500 | '0', 501 | '1', 502 | ]); 503 | } 504 | 505 | private function makePassword(string $value) : string 506 | { 507 | return $this->quote($value); 508 | } 509 | 510 | private function makeStatsAutoRecalc(string $value) : string 511 | { 512 | return $this->getValue(static::OPT_STATS_AUTO_RECALC, $value, [ 513 | '0', 514 | '1', 515 | 'DEFAULT', 516 | ], \strtoupper($value)); 517 | } 518 | 519 | private function makeStatsPersistent(string $value) : string 520 | { 521 | return $this->getValue(static::OPT_STATS_PERSISTENT, $value, [ 522 | '0', 523 | '1', 524 | 'DEFAULT', 525 | ], \strtoupper($value)); 526 | } 527 | 528 | private function makeStatsSamplePages(string $value) : string 529 | { 530 | if (\strtoupper($value) === 'DEFAULT') { 531 | return 'DEFAULT'; 532 | } 533 | if (\is_numeric($value) && $value > 0) { 534 | return $value; 535 | } 536 | throw $this->invalidValue(static::OPT_STATS_SAMPLE_PAGES, $value); 537 | } 538 | 539 | private function makeTablespace(string $value) : string 540 | { 541 | return $this->quote($value); 542 | } 543 | 544 | private function makeRowFormat(string $value) : string 545 | { 546 | return $this->getValue(static::OPT_ROW_FORMAT, $value, [ 547 | 'COMPACT', 548 | 'COMPRESSED', 549 | 'DEFAULT', 550 | 'DYNAMIC', 551 | 'FIXED', 552 | 'PAGE', 553 | 'REDUNDANT', 554 | ], \strtoupper($value)); 555 | } 556 | 557 | private function makeSequence(string $value) : string 558 | { 559 | if (\is_numeric($value) && $value >= 0) { 560 | return $value; 561 | } 562 | throw $this->invalidValue(static::OPT_SEQUENCE, $value); 563 | } 564 | 565 | private function makeTransactional(string $value) : string 566 | { 567 | return $this->getValue(static::OPT_TRANSACTIONAL, $value, [ 568 | '0', 569 | '1', 570 | ]); 571 | } 572 | 573 | private function makeUnion(string $value) : string 574 | { 575 | if ($value === '') { 576 | throw $this->invalidValue(static::OPT_UNION, $value); 577 | } 578 | $tables = \array_map('trim', \explode(',', $value)); 579 | foreach ($tables as &$table) { 580 | $table = $this->database->protectIdentifier($table); 581 | } 582 | unset($table); 583 | $tables = \implode(', ', $tables); 584 | return '(' . $tables . ')'; 585 | } 586 | 587 | /** 588 | * @param string $optionName 589 | * @param string $originalValue 590 | * @param array $options 591 | * @param string|null $value 592 | * @param bool $getByKey 593 | * 594 | * @return string 595 | */ 596 | private function getValue( 597 | string $optionName, 598 | string $originalValue, 599 | array $options, 600 | string $value = null, 601 | bool $getByKey = false 602 | ) : string { 603 | $value ??= $originalValue; 604 | if ($getByKey) { 605 | if (isset($options[$value])) { 606 | return $options[$value]; 607 | } 608 | } elseif (\in_array($value, $options, true)) { 609 | return $value; 610 | } 611 | throw $this->invalidValue($optionName, $originalValue); 612 | } 613 | 614 | private function invalidValue(string $option, string $value) : InvalidArgumentException 615 | { 616 | return new InvalidArgumentException("Invalid {$option} option value: {$value}"); 617 | } 618 | 619 | private function quote(string $value) : string 620 | { 621 | return (string) $this->database->quote($value); 622 | } 623 | } 624 | --------------------------------------------------------------------------------