diff --git a/composer.json b/composer.json
index 7158ebdf..abac4455 100644
--- a/composer.json
+++ b/composer.json
@@ -19,7 +19,8 @@
"pguardiario/phpuri": "^1.0",
"symfony/lock": "^4.1",
"ezyang/htmlpurifier": "^4.10",
- "xemlock/htmlpurifier-html5": "^0.1.7"
+ "xemlock/htmlpurifier-html5": "^0.1.7",
+ "catfan/medoo": "^1.5"
},
"autoload": {
"psr-4": {
diff --git a/composer.lock b/composer.lock
index 3c556fb3..38688521 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "content-hash": "43da7d5e9d8441faa4311f32b16ee1c0",
+ "content-hash": "3fceaf3c2925068c7e757ab12036e29e",
"packages": [
{
"name": "brandonwamboldt/utilphp",
@@ -52,6 +52,65 @@
],
"time": "2015-02-02T17:56:14+00:00"
},
+ {
+ "name": "catfan/medoo",
+ "version": "v1.5.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/catfan/Medoo.git",
+ "reference": "8d90cba0e8ff176028847527d0ea76fe41a06ecf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/catfan/Medoo/zipball/8d90cba0e8ff176028847527d0ea76fe41a06ecf",
+ "reference": "8d90cba0e8ff176028847527d0ea76fe41a06ecf",
+ "shasum": ""
+ },
+ "require": {
+ "ext-pdo": "*",
+ "php": ">=5.4"
+ },
+ "suggest": {
+ "ext-pdo_dblib": "For MSSQL or Sybase database on Linux/UNIX platform",
+ "ext-pdo_mysql": "For MySQL or MariaDB database",
+ "ext-pdo_oci": "For Oracle database",
+ "ext-pdo_oci8": "For Oracle version 8 database",
+ "ext-pdo_pqsql": "For PostgreSQL database",
+ "ext-pdo_sqlite": "For SQLite database",
+ "ext-pdo_sqlsrv": "For MSSQL database"
+ },
+ "type": "framework",
+ "autoload": {
+ "psr-4": {
+ "Medoo\\": "/src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Angel Lai",
+ "email": "angel@catfan.me"
+ }
+ ],
+ "description": "The lightest PHP database framework to accelerate development",
+ "homepage": "https://medoo.in",
+ "keywords": [
+ "database",
+ "lightweight",
+ "mariadb",
+ "mssql",
+ "mysql",
+ "oracle",
+ "php framework",
+ "postgresql",
+ "sql",
+ "sqlite"
+ ],
+ "time": "2018-06-14T18:59:08+00:00"
+ },
{
"name": "ezyang/htmlpurifier",
"version": "v4.10.0",
@@ -318,16 +377,16 @@
},
{
"name": "leafo/scssphp",
- "version": "v0.7.6",
+ "version": "v0.7.7",
"source": {
"type": "git",
"url": "https://github.com/leafo/scssphp.git",
- "reference": "585f6ae84de62ffecf69c23805f25d78d7e4b794"
+ "reference": "1d656f8c02a3a69404bba6b28ec4e06edddf0f49"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/leafo/scssphp/zipball/585f6ae84de62ffecf69c23805f25d78d7e4b794",
- "reference": "585f6ae84de62ffecf69c23805f25d78d7e4b794",
+ "url": "https://api.github.com/repos/leafo/scssphp/zipball/1d656f8c02a3a69404bba6b28ec4e06edddf0f49",
+ "reference": "1d656f8c02a3a69404bba6b28ec4e06edddf0f49",
"shasum": ""
},
"require": {
@@ -366,7 +425,7 @@
"scss",
"stylesheet"
],
- "time": "2018-05-24T02:18:53+00:00"
+ "time": "2018-07-22T01:22:08+00:00"
},
{
"name": "league/plates",
@@ -751,16 +810,16 @@
},
{
"name": "symfony/lock",
- "version": "v4.1.0",
+ "version": "v4.1.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/lock.git",
- "reference": "a1df50492e927ad00ca271f29adb2949a14f2a5a"
+ "reference": "d508b5dd92272cb74300396ecaadde969d0f7976"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/lock/zipball/a1df50492e927ad00ca271f29adb2949a14f2a5a",
- "reference": "a1df50492e927ad00ca271f29adb2949a14f2a5a",
+ "url": "https://api.github.com/repos/symfony/lock/zipball/d508b5dd92272cb74300396ecaadde969d0f7976",
+ "reference": "d508b5dd92272cb74300396ecaadde969d0f7976",
"shasum": ""
},
"require": {
@@ -808,7 +867,7 @@
"redlock",
"semaphore"
],
- "time": "2018-05-30T07:26:09+00:00"
+ "time": "2018-06-21T11:15:46+00:00"
},
{
"name": "vlucas/valitron",
diff --git a/vendor/catfan/medoo/.gitattributes b/vendor/catfan/medoo/.gitattributes
new file mode 100644
index 00000000..8079ae23
--- /dev/null
+++ b/vendor/catfan/medoo/.gitattributes
@@ -0,0 +1,22 @@
+# Auto detect text files and perform LF normalization
+* text=auto
+
+# Custom for Visual Studio
+*.cs diff=csharp
+*.sln merge=union
+*.csproj merge=union
+*.vbproj merge=union
+*.fsproj merge=union
+*.dbproj merge=union
+
+# Standard to msysgit
+*.doc diff=astextplain
+*.DOC diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot diff=astextplain
+*.DOT diff=astextplain
+*.pdf diff=astextplain
+*.PDF diff=astextplain
+*.rtf diff=astextplain
+*.RTF diff=astextplain
\ No newline at end of file
diff --git a/vendor/catfan/medoo/.gitignore b/vendor/catfan/medoo/.gitignore
new file mode 100644
index 00000000..0c814575
--- /dev/null
+++ b/vendor/catfan/medoo/.gitignore
@@ -0,0 +1,216 @@
+#################
+## Eclipse
+#################
+
+*.pydevproject
+.project
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.classpath
+.settings/
+.loadpath
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+
+#################
+## Visual Studio
+#################
+
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+
+[Dd]ebug/
+[Rr]elease/
+x64/
+build/
+[Bb]in/
+[Oo]bj/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+*_i.c
+*_p.c
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.log
+*.scc
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+*.ncrunch*
+.*crunch*.local.xml
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.Publish.xml
+*.pubxml
+*.publishproj
+
+# NuGet Packages Directory
+## TODO: If you have NuGet Package Restore enabled, uncomment the next line
+#packages/
+
+# Windows Azure Build Output
+csx
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.[Pp]ublish.xml
+*.pfx
+*.publishsettings
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file to a newer
+# Visual Studio version. Backup files are not needed, because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+App_Data/*.mdf
+App_Data/*.ldf
+
+#############
+## Windows detritus
+#############
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Mac crap
+.DS_Store
+
+
+#############
+## Python
+#############
+
+*.py[cod]
+
+# Packages
+*.egg
+*.egg-info
+dist/
+build/
+eggs/
+parts/
+var/
+sdist/
+develop-eggs/
+.installed.cfg
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+
+#Translations
+*.mo
+
+#Mr Developer
+.mr.developer.cfg
\ No newline at end of file
diff --git a/vendor/catfan/medoo/LICENSE.md b/vendor/catfan/medoo/LICENSE.md
new file mode 100644
index 00000000..a681c880
--- /dev/null
+++ b/vendor/catfan/medoo/LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Angel Lai
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/catfan/medoo/README.md b/vendor/catfan/medoo/README.md
new file mode 100644
index 00000000..f31e1d06
--- /dev/null
+++ b/vendor/catfan/medoo/README.md
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+> The Lightest PHP database framework to accelerate development
+
+## Features
+
+* **Lightweight** - Less than 100 KB, portable with only one file
+
+* **Easy** - Extremely easy to learn and use, friendly construction
+
+* **Powerful** - Supports various common and complex SQL queries, data mapping, and prevent SQL injection
+
+* **Compatible** - Supports all SQL databases, including MySQL, MSSQL, SQLite, MariaDB, PostgreSQL, Sybase, Oracle and more
+
+* **Friendly** - Works well with every PHP frameworks, like Laravel, Codeigniter, Yii, Slim, and framework which supports singleton extension or composer
+
+* **Free** - Under MIT license, you can use it anywhere whatever you want
+
+## Requirement
+
+PHP 5.4+ and PDO extension installed
+
+## Get Started
+
+### Install via composer
+
+Add Medoo to composer.json configuration file.
+```
+$ composer require catfan/Medoo
+```
+
+And update the composer
+```
+$ composer update
+```
+
+```php
+// If you installed via composer, just use this code to requrie autoloader on the top of your projects.
+require 'vendor/autoload.php';
+
+// Using Medoo namespace
+use Medoo\Medoo;
+
+// Initialize
+$database = new Medoo([
+ 'database_type' => 'mysql',
+ 'database_name' => 'name',
+ 'server' => 'localhost',
+ 'username' => 'your_username',
+ 'password' => 'your_password'
+]);
+
+// Enjoy
+$database->insert('account', [
+ 'user_name' => 'foo',
+ 'email' => 'foo@bar.com'
+]);
+
+$data = $database->select('account', [
+ 'user_name',
+ 'email'
+], [
+ 'user_id' => 50
+]);
+
+echo json_encode($data);
+
+// [
+// {
+// "user_name" : "foo",
+// "email" : "foo@bar.com",
+// }
+// ]
+```
+
+## Contribution Guides
+
+For most of time, Medoo is using develop branch for adding feature and fixing bug, and the branch will be merged into master branch while releasing a public version. For contribution, submit your code to the develop branch, and start a pull request into it.
+
+On develop branch, each commits are started with `[fix]`, `[feature]` or `[update]` tag to indicate the change.
+
+Keep it simple and keep it clear.
+
+## License
+
+Medoo is under the MIT license.
+
+## Links
+
+* Official website: [https://medoo.in](https://medoo.in)
+
+* Documentation: [https://medoo.in/doc](https://medoo.in/doc)
\ No newline at end of file
diff --git a/vendor/catfan/medoo/composer.json b/vendor/catfan/medoo/composer.json
new file mode 100644
index 00000000..39c1a9de
--- /dev/null
+++ b/vendor/catfan/medoo/composer.json
@@ -0,0 +1,33 @@
+{
+ "name": "catfan/medoo",
+ "type": "framework",
+ "description": "The lightest PHP database framework to accelerate development",
+ "keywords": ["database", "lightweight", "PHP framework", "SQL", "MySQL", "MSSQL", "SQLite", "PostgreSQL", "MariaDB", "Oracle"],
+ "homepage": "https://medoo.in",
+ "license": "MIT",
+ "support": {
+ "issues": "https://github.com/catfan/Medoo/issues",
+ "source": "https://github.com/catfan/Medoo"
+ },
+ "authors": [
+ {"name": "Angel Lai", "email": "angel@catfan.me"}
+ ],
+ "require": {
+ "php": ">=5.4",
+ "ext-pdo": "*"
+ },
+ "suggest": {
+ "ext-pdo_mysql": "For MySQL or MariaDB database",
+ "ext-pdo_sqlsrv": "For MSSQL database",
+ "ext-pdo_dblib": "For MSSQL or Sybase database on Linux/UNIX platform",
+ "ext-pdo_oci": "For Oracle database",
+ "ext-pdo_oci8": "For Oracle version 8 database",
+ "ext-pdo_pqsql": "For PostgreSQL database",
+ "ext-pdo_sqlite": "For SQLite database"
+ },
+ "autoload": {
+ "psr-4": {
+ "Medoo\\": "/src"
+ }
+ }
+}
\ No newline at end of file
diff --git a/vendor/catfan/medoo/src/Medoo.php b/vendor/catfan/medoo/src/Medoo.php
new file mode 100644
index 00000000..b97b8b8c
--- /dev/null
+++ b/vendor/catfan/medoo/src/Medoo.php
@@ -0,0 +1,1553 @@
+type = strtolower($options[ 'database_type' ]);
+ }
+
+ if (isset($options[ 'prefix' ]))
+ {
+ $this->prefix = $options[ 'prefix' ];
+ }
+
+ if (isset($options[ 'option' ]))
+ {
+ $this->option = $options[ 'option' ];
+ }
+
+ if (isset($options[ 'logging' ]) && is_bool($options[ 'logging' ]))
+ {
+ $this->logging = $options[ 'logging' ];
+ }
+
+ if (isset($options[ 'command' ]) && is_array($options[ 'command' ]))
+ {
+ $commands = $options[ 'command' ];
+ }
+ else
+ {
+ $commands = [];
+ }
+
+ if (isset($options[ 'dsn' ]))
+ {
+ if (is_array($options[ 'dsn' ]) && isset($options[ 'dsn' ][ 'driver' ]))
+ {
+ $attr = $options[ 'dsn' ];
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (
+ isset($options[ 'port' ]) &&
+ is_int($options[ 'port' ] * 1)
+ )
+ {
+ $port = $options[ 'port' ];
+ }
+
+ $is_port = isset($port);
+
+ switch ($this->type)
+ {
+ case 'mariadb':
+ case 'mysql':
+ $attr = [
+ 'driver' => 'mysql',
+ 'dbname' => $options[ 'database_name' ]
+ ];
+
+ if (isset($options[ 'socket' ]))
+ {
+ $attr[ 'unix_socket' ] = $options[ 'socket' ];
+ }
+ else
+ {
+ $attr[ 'host' ] = $options[ 'server' ];
+
+ if ($is_port)
+ {
+ $attr[ 'port' ] = $port;
+ }
+ }
+
+ // Make MySQL using standard quoted identifier
+ $commands[] = 'SET SQL_MODE=ANSI_QUOTES';
+ break;
+
+ case 'pgsql':
+ $attr = [
+ 'driver' => 'pgsql',
+ 'host' => $options[ 'server' ],
+ 'dbname' => $options[ 'database_name' ]
+ ];
+
+ if ($is_port)
+ {
+ $attr[ 'port' ] = $port;
+ }
+
+ break;
+
+ case 'sybase':
+ $attr = [
+ 'driver' => 'dblib',
+ 'host' => $options[ 'server' ],
+ 'dbname' => $options[ 'database_name' ]
+ ];
+
+ if ($is_port)
+ {
+ $attr[ 'port' ] = $port;
+ }
+
+ break;
+
+ case 'oracle':
+ $attr = [
+ 'driver' => 'oci',
+ 'dbname' => $options[ 'server' ] ?
+ '//' . $options[ 'server' ] . ($is_port ? ':' . $port : ':1521') . '/' . $options[ 'database_name' ] :
+ $options[ 'database_name' ]
+ ];
+
+ if (isset($options[ 'charset' ]))
+ {
+ $attr[ 'charset' ] = $options[ 'charset' ];
+ }
+
+ break;
+
+ case 'mssql':
+ if (isset($options[ 'driver' ]) && $options[ 'driver' ] === 'dblib')
+ {
+ $attr = [
+ 'driver' => 'dblib',
+ 'host' => $options[ 'server' ] . ($is_port ? ':' . $port : ''),
+ 'dbname' => $options[ 'database_name' ]
+ ];
+ }
+ else
+ {
+ $attr = [
+ 'driver' => 'sqlsrv',
+ 'Server' => $options[ 'server' ] . ($is_port ? ',' . $port : ''),
+ 'Database' => $options[ 'database_name' ]
+ ];
+ }
+
+ // Keep MSSQL QUOTED_IDENTIFIER is ON for standard quoting
+ $commands[] = 'SET QUOTED_IDENTIFIER ON';
+
+ // Make ANSI_NULLS is ON for NULL value
+ $commands[] = 'SET ANSI_NULLS ON';
+ break;
+
+ case 'sqlite':
+ $attr = [
+ 'driver' => 'sqlite',
+ $options[ 'database_file' ]
+ ];
+
+ break;
+ }
+ }
+
+ $driver = $attr[ 'driver' ];
+
+ unset($attr[ 'driver' ]);
+
+ $stack = [];
+
+ foreach ($attr as $key => $value)
+ {
+ $stack[] = is_int($key) ? $value : $key . '=' . $value;
+ }
+
+ $dsn = $driver . ':' . implode($stack, ';');
+
+ if (
+ in_array($this->type, ['mariadb', 'mysql', 'pgsql', 'sybase', 'mssql']) &&
+ isset($options[ 'charset' ])
+ )
+ {
+ $commands[] = "SET NAMES '" . $options[ 'charset' ] . "'";
+ }
+
+ try {
+ $this->pdo = new PDO(
+ $dsn,
+ isset($options[ 'username' ]) ? $options[ 'username' ] : null,
+ isset($options[ 'password' ]) ? $options[ 'password' ] : null,
+ $this->option
+ );
+
+ foreach ($commands as $value)
+ {
+ $this->pdo->exec($value);
+ }
+ }
+ catch (PDOException $e) {
+ throw new PDOException($e->getMessage());
+ }
+ }
+
+ public function __call($name, $arguments)
+ {
+ $aggregation = ['avg', 'count', 'max', 'min', 'sum'];
+
+ if (in_array($name, $aggregation))
+ {
+ array_unshift($arguments, $name);
+
+ return call_user_func_array([$this, 'aggregate'], $arguments);
+ }
+ }
+
+ public function query($query, $map = [])
+ {
+ $raw = $this->raw($query, $map);
+
+ $query = $this->buildRaw($raw, $map);
+
+ return $this->exec($query, $map);
+ }
+
+ public function exec($query, $map = [])
+ {
+ if ($this->debug_mode)
+ {
+ echo $this->generate($query, $map);
+
+ $this->debug_mode = false;
+
+ return false;
+ }
+
+ if ($this->logging)
+ {
+ $this->logs[] = [$query, $map];
+ }
+ else
+ {
+ $this->logs = [[$query, $map]];
+ }
+
+ $statement = $this->pdo->prepare($query);
+
+ if ($statement)
+ {
+ foreach ($map as $key => $value)
+ {
+ $statement->bindValue($key, $value[ 0 ], $value[ 1 ]);
+ }
+
+ $statement->execute();
+
+ $this->statement = $statement;
+
+ return $statement;
+ }
+
+ return false;
+ }
+
+ protected function generate($query, $map)
+ {
+ $identifier = [
+ 'mysql' => '`$1`',
+ 'mariadb' => '`$1`',
+ 'mssql' => '[$1]'
+ ];
+
+ $query = preg_replace(
+ '/"([a-zA-Z0-9_]+)"/i',
+ isset($identifier[ $this->type ]) ? $identifier[ $this->type ] : '"$1"',
+ $query
+ );
+
+ foreach ($map as $key => $value)
+ {
+ if ($value[ 1 ] === PDO::PARAM_STR)
+ {
+ $replace = $this->quote($value[ 0 ]);
+ }
+ elseif ($value[ 1 ] === PDO::PARAM_NULL)
+ {
+ $replace = 'NULL';
+ }
+ elseif ($value[ 1 ] === PDO::PARAM_LOB)
+ {
+ $replace = '{LOB_DATA}';
+ }
+ else
+ {
+ $replace = $value[ 0 ];
+ }
+
+ $query = str_replace($key, $replace, $query);
+ }
+
+ return $query;
+ }
+
+ public static function raw($string, $map = [])
+ {
+ $raw = new Raw();
+
+ $raw->map = $map;
+ $raw->value = $string;
+
+ return $raw;
+ }
+
+ protected function isRaw($object)
+ {
+ return $object instanceof Raw;
+ }
+
+ protected function buildRaw($raw, &$map)
+ {
+ if (!$this->isRaw($raw))
+ {
+ return false;
+ }
+
+ $query = preg_replace_callback(
+ '/((FROM|TABLE|INTO|UPDATE)\s*)?\<([a-zA-Z0-9_\.]+)\>/i',
+ function ($matches)
+ {
+ if (!empty($matches[ 2 ]))
+ {
+ return $matches[ 2 ] . ' ' . $this->tableQuote($matches[ 3 ]);
+ }
+
+ return $this->columnQuote($matches[ 3 ]);
+ },
+ $raw->value);
+
+ $raw_map = $raw->map;
+
+ if (!empty($raw_map))
+ {
+ foreach ($raw_map as $key => $value)
+ {
+ $map[ $key ] = $this->typeMap($value, gettype($value));
+ }
+ }
+
+ return $query;
+ }
+
+ public function quote($string)
+ {
+ return $this->pdo->quote($string);
+ }
+
+ protected function tableQuote($table)
+ {
+ return '"' . $this->prefix . $table . '"';
+ }
+
+ protected function mapKey()
+ {
+ return ':MeDoO_' . $this->guid++ . '_mEdOo';
+ }
+
+ protected function typeMap($value, $type)
+ {
+ $map = [
+ 'NULL' => PDO::PARAM_NULL,
+ 'integer' => PDO::PARAM_INT,
+ 'double' => PDO::PARAM_STR,
+ 'boolean' => PDO::PARAM_BOOL,
+ 'string' => PDO::PARAM_STR,
+ 'object' => PDO::PARAM_STR,
+ 'resource' => PDO::PARAM_LOB
+ ];
+
+ if ($type === 'boolean')
+ {
+ $value = ($value ? '1' : '0');
+ }
+ elseif ($type === 'NULL')
+ {
+ $value = null;
+ }
+
+ return [$value, $map[ $type ]];
+ }
+
+ protected function columnQuote($string)
+ {
+ if (strpos($string, '.') !== false)
+ {
+ return '"' . $this->prefix . str_replace('.', '"."', $string) . '"';
+ }
+
+ return '"' . $string . '"';
+ }
+
+ protected function columnPush(&$columns, &$map)
+ {
+ if ($columns === '*')
+ {
+ return $columns;
+ }
+
+ $stack = [];
+
+ if (is_string($columns))
+ {
+ $columns = [$columns];
+ }
+
+ foreach ($columns as $key => $value)
+ {
+ if (is_array($value))
+ {
+ $stack[] = $this->columnPush($value, $map);
+ }
+ elseif (!is_int($key) && $raw = $this->buildRaw($value, $map))
+ {
+ preg_match('/(?[a-zA-Z0-9_\.]+)(\s*\[(?(String|Bool|Int|Number))\])?/i', $key, $match);
+
+ $stack[] = $raw . ' AS ' . $this->columnQuote( $match[ 'column' ] );
+ }
+ elseif (is_int($key) && is_string($value))
+ {
+ preg_match('/(?[a-zA-Z0-9_\.]+)(?:\s*\((?[a-zA-Z0-9_]+)\))?(?:\s*\[(?(?:String|Bool|Int|Number|Object|JSON))\])?/i', $value, $match);
+
+ if (!empty($match[ 'alias' ]))
+ {
+ $stack[] = $this->columnQuote( $match[ 'column' ] ) . ' AS ' . $this->columnQuote( $match[ 'alias' ] );
+
+ $columns[ $key ] = $match[ 'alias' ];
+
+ if (!empty($match[ 'type' ]))
+ {
+ $columns[ $key ] .= ' [' . $match[ 'type' ] . ']';
+ }
+ }
+ else
+ {
+ $stack[] = $this->columnQuote( $match[ 'column' ] );
+ }
+ }
+ }
+
+ return implode($stack, ',');
+ }
+
+ protected function arrayQuote($array)
+ {
+ $stack = [];
+
+ foreach ($array as $value)
+ {
+ $stack[] = is_int($value) ? $value : $this->pdo->quote($value);
+ }
+
+ return implode($stack, ',');
+ }
+
+ protected function innerConjunct($data, $map, $conjunctor, $outer_conjunctor)
+ {
+ $stack = [];
+
+ foreach ($data as $value)
+ {
+ $stack[] = '(' . $this->dataImplode($value, $map, $conjunctor) . ')';
+ }
+
+ return implode($outer_conjunctor . ' ', $stack);
+ }
+
+ protected function dataImplode($data, &$map, $conjunctor)
+ {
+ $stack = [];
+
+ foreach ($data as $key => $value)
+ {
+ $type = gettype($value);
+
+ if (
+ $type === 'array' &&
+ preg_match("/^(AND|OR)(\s+#.*)?$/", $key, $relation_match)
+ )
+ {
+ $relationship = $relation_match[ 1 ];
+
+ $stack[] = $value !== array_keys(array_keys($value)) ?
+ '(' . $this->dataImplode($value, $map, ' ' . $relationship) . ')' :
+ '(' . $this->innerConjunct($value, $map, ' ' . $relationship, $conjunctor) . ')';
+
+ continue;
+ }
+
+ $map_key = $this->mapKey();
+
+ if (
+ is_int($key) &&
+ preg_match('/([a-zA-Z0-9_\.]+)\[(?\>\=?|\<\=?|\!?\=)\]([a-zA-Z0-9_\.]+)/i', $value, $match)
+ )
+ {
+ $stack[] = $this->columnQuote($match[ 1 ]) . ' ' . $match[ 'operator' ] . ' ' . $this->columnQuote($match[ 3 ]);
+ }
+ else
+ {
+ preg_match('/([a-zA-Z0-9_\.]+)(\[(?\>\=?|\<\=?|\!|\<\>|\>\<|\!?~|REGEXP)\])?/i', $key, $match);
+ $column = $this->columnQuote($match[ 1 ]);
+
+ if (isset($match[ 'operator' ]))
+ {
+ $operator = $match[ 'operator' ];
+
+ if (in_array($operator, ['>', '>=', '<', '<=']))
+ {
+ $condition = $column . ' ' . $operator . ' ';
+
+ if (is_numeric($value))
+ {
+ $condition .= $map_key;
+ $map[ $map_key ] = [$value, PDO::PARAM_INT];
+ }
+ elseif ($raw = $this->buildRaw($value, $map))
+ {
+ $condition .= $raw;
+ }
+ else
+ {
+ $condition .= $map_key;
+ $map[ $map_key ] = [$value, PDO::PARAM_STR];
+ }
+
+ $stack[] = $condition;
+ }
+ elseif ($operator === '!')
+ {
+ switch ($type)
+ {
+ case 'NULL':
+ $stack[] = $column . ' IS NOT NULL';
+ break;
+
+ case 'array':
+ $placeholders = [];
+
+ foreach ($value as $index => $item)
+ {
+ $placeholders[] = $map_key . $index . '_i';
+ $map[ $map_key . $index . '_i' ] = $this->typeMap($item, gettype($item));
+ }
+
+ $stack[] = $column . ' NOT IN (' . implode(', ', $placeholders) . ')';
+ break;
+
+ case 'object':
+ if ($raw = $this->buildRaw($value, $map))
+ {
+ $stack[] = $column . ' != ' . $raw;
+ }
+ break;
+
+ case 'integer':
+ case 'double':
+ case 'boolean':
+ case 'string':
+ $stack[] = $column . ' != ' . $map_key;
+ $map[ $map_key ] = $this->typeMap($value, $type);
+ break;
+ }
+ }
+ elseif ($operator === '~' || $operator === '!~')
+ {
+ if ($type !== 'array')
+ {
+ $value = [ $value ];
+ }
+
+ $connector = ' OR ';
+ $data = array_values($value);
+
+ if (is_array($data[ 0 ]))
+ {
+ if (isset($value[ 'AND' ]) || isset($value[ 'OR' ]))
+ {
+ $connector = ' ' . array_keys($value)[ 0 ] . ' ';
+ $value = $data[ 0 ];
+ }
+ }
+
+ $like_clauses = [];
+
+ foreach ($value as $index => $item)
+ {
+ $item = strval($item);
+
+ if (!preg_match('/(\[.+\]|_|%.+|.+%)/', $item))
+ {
+ $item = '%' . $item . '%';
+ }
+
+ $like_clauses[] = $column . ($operator === '!~' ? ' NOT' : '') . ' LIKE ' . $map_key . 'L' . $index;
+ $map[ $map_key . 'L' . $index ] = [$item, PDO::PARAM_STR];
+ }
+
+ $stack[] = '(' . implode($connector, $like_clauses) . ')';
+ }
+ elseif ($operator === '<>' || $operator === '><')
+ {
+ if ($type === 'array')
+ {
+ if ($operator === '><')
+ {
+ $column .= ' NOT';
+ }
+
+ $stack[] = '(' . $column . ' BETWEEN ' . $map_key . 'a AND ' . $map_key . 'b)';
+
+ $data_type = (is_numeric($value[ 0 ]) && is_numeric($value[ 1 ])) ? PDO::PARAM_INT : PDO::PARAM_STR;
+
+ $map[ $map_key . 'a' ] = [$value[ 0 ], $data_type];
+ $map[ $map_key . 'b' ] = [$value[ 1 ], $data_type];
+ }
+ }
+ elseif ($operator === 'REGEXP')
+ {
+ $stack[] = $column . ' REGEXP ' . $map_key;
+ $map[ $map_key ] = [$value, PDO::PARAM_STR];
+ }
+ }
+ else
+ {
+ switch ($type)
+ {
+ case 'NULL':
+ $stack[] = $column . ' IS NULL';
+ break;
+
+ case 'array':
+ $placeholders = [];
+
+ foreach ($value as $index => $item)
+ {
+ $placeholders[] = $map_key . $index . '_i';
+ $map[ $map_key . $index . '_i' ] = $this->typeMap($item, gettype($item));
+ }
+
+ $stack[] = $column . ' IN (' . implode(', ', $placeholders) . ')';
+ break;
+
+ case 'object':
+ if ($raw = $this->buildRaw($value, $map))
+ {
+ $stack[] = $column . ' = ' . $raw;
+ }
+ break;
+
+ case 'integer':
+ case 'double':
+ case 'boolean':
+ case 'string':
+ $stack[] = $column . ' = ' . $map_key;
+ $map[ $map_key ] = $this->typeMap($value, $type);
+ break;
+ }
+ }
+ }
+ }
+
+ return implode($conjunctor . ' ', $stack);
+ }
+
+ protected function whereClause($where, &$map)
+ {
+ $where_clause = '';
+
+ if (is_array($where))
+ {
+ $where_keys = array_keys($where);
+
+ $conditions = array_diff_key($where, array_flip(
+ ['GROUP', 'ORDER', 'HAVING', 'LIMIT', 'LIKE', 'MATCH']
+ ));
+
+ if (!empty($conditions))
+ {
+ $where_clause = ' WHERE ' . $this->dataImplode($conditions, $map, ' AND');
+ }
+
+ if (isset($where[ 'MATCH' ]))
+ {
+ $MATCH = $where[ 'MATCH' ];
+
+ if (is_array($MATCH) && isset($MATCH[ 'columns' ], $MATCH[ 'keyword' ]))
+ {
+ $mode = '';
+
+ $mode_array = [
+ 'natural' => 'IN NATURAL LANGUAGE MODE',
+ 'natural+query' => 'IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION',
+ 'boolean' => 'IN BOOLEAN MODE',
+ 'query' => 'WITH QUERY EXPANSION'
+ ];
+
+ if (isset($MATCH[ 'mode' ], $mode_array[ $MATCH[ 'mode' ] ]))
+ {
+ $mode = ' ' . $mode_array[ $MATCH[ 'mode' ] ];
+ }
+
+ $columns = implode(array_map([$this, 'columnQuote'], $MATCH[ 'columns' ]), ', ');
+ $map_key = $this->mapKey();
+ $map[ $map_key ] = [$MATCH[ 'keyword' ], PDO::PARAM_STR];
+
+ $where_clause .= ($where_clause !== '' ? ' AND ' : ' WHERE') . ' MATCH (' . $columns . ') AGAINST (' . $map_key . $mode . ')';
+ }
+ }
+
+ if (isset($where[ 'GROUP' ]))
+ {
+ $GROUP = $where[ 'GROUP' ];
+
+ if (is_array($GROUP))
+ {
+ $stack = [];
+
+ foreach ($GROUP as $column => $value)
+ {
+ $stack[] = $this->columnQuote($value);
+ }
+
+ $where_clause .= ' GROUP BY ' . implode($stack, ',');
+ }
+ elseif ($raw = $this->buildRaw($GROUP, $map))
+ {
+ $where_clause .= ' GROUP BY ' . $raw;
+ }
+ else
+ {
+ $where_clause .= ' GROUP BY ' . $this->columnQuote($GROUP);
+ }
+
+ if (isset($where[ 'HAVING' ]))
+ {
+ if ($raw = $this->buildRaw($where[ 'HAVING' ], $map))
+ {
+ $where_clause .= ' HAVING ' . $raw;
+ }
+ else
+ {
+ $where_clause .= ' HAVING ' . $this->dataImplode($where[ 'HAVING' ], $map, ' AND');
+ }
+ }
+ }
+
+ if (isset($where[ 'ORDER' ]))
+ {
+ $ORDER = $where[ 'ORDER' ];
+
+ if (is_array($ORDER))
+ {
+ $stack = [];
+
+ foreach ($ORDER as $column => $value)
+ {
+ if (is_array($value))
+ {
+ $stack[] = 'FIELD(' . $this->columnQuote($column) . ', ' . $this->arrayQuote($value) . ')';
+ }
+ elseif ($value === 'ASC' || $value === 'DESC')
+ {
+ $stack[] = $this->columnQuote($column) . ' ' . $value;
+ }
+ elseif (is_int($column))
+ {
+ $stack[] = $this->columnQuote($value);
+ }
+ }
+
+ $where_clause .= ' ORDER BY ' . implode($stack, ',');
+ }
+ elseif ($raw = $this->buildRaw($ORDER, $map))
+ {
+ $where_clause .= ' ORDER BY ' . $raw;
+ }
+ else
+ {
+ $where_clause .= ' ORDER BY ' . $this->columnQuote($ORDER);
+ }
+
+ if (
+ isset($where[ 'LIMIT' ]) &&
+ in_array($this->type, ['oracle', 'mssql'])
+ )
+ {
+ $LIMIT = $where[ 'LIMIT' ];
+
+ if (is_numeric($LIMIT))
+ {
+ $LIMIT = [0, $LIMIT];
+ }
+
+ if (
+ is_array($LIMIT) &&
+ is_numeric($LIMIT[ 0 ]) &&
+ is_numeric($LIMIT[ 1 ])
+ )
+ {
+ $where_clause .= ' OFFSET ' . $LIMIT[ 0 ] . ' ROWS FETCH NEXT ' . $LIMIT[ 1 ] . ' ROWS ONLY';
+ }
+ }
+ }
+
+ if (isset($where[ 'LIMIT' ]) && !in_array($this->type, ['oracle', 'mssql']))
+ {
+ $LIMIT = $where[ 'LIMIT' ];
+
+ if (is_numeric($LIMIT))
+ {
+ $where_clause .= ' LIMIT ' . $LIMIT;
+ }
+ elseif (
+ is_array($LIMIT) &&
+ is_numeric($LIMIT[ 0 ]) &&
+ is_numeric($LIMIT[ 1 ])
+ )
+ {
+ $where_clause .= ' LIMIT ' . $LIMIT[ 1 ] . ' OFFSET ' . $LIMIT[ 0 ];
+ }
+ }
+ }
+ elseif ($raw = $this->buildRaw($where, $map))
+ {
+ $where_clause .= ' ' . $raw;
+ }
+
+ return $where_clause;
+ }
+
+ protected function selectContext($table, &$map, $join, &$columns = null, $where = null, $column_fn = null)
+ {
+ preg_match('/(?[a-zA-Z0-9_]+)\s*\((?[a-zA-Z0-9_]+)\)/i', $table, $table_match);
+
+ if (isset($table_match[ 'table' ], $table_match[ 'alias' ]))
+ {
+ $table = $this->tableQuote($table_match[ 'table' ]);
+
+ $table_query = $table . ' AS ' . $this->tableQuote($table_match[ 'alias' ]);
+ }
+ else
+ {
+ $table = $this->tableQuote($table);
+
+ $table_query = $table;
+ }
+
+ $join_key = is_array($join) ? array_keys($join) : null;
+
+ if (
+ isset($join_key[ 0 ]) &&
+ strpos($join_key[ 0 ], '[') === 0
+ )
+ {
+ $table_join = [];
+
+ $join_array = [
+ '>' => 'LEFT',
+ '<' => 'RIGHT',
+ '<>' => 'FULL',
+ '><' => 'INNER'
+ ];
+
+ foreach($join as $sub_table => $relation)
+ {
+ preg_match('/(\[(?\<\>?|\>\)\])?(?[a-zA-Z0-9_]+)\s?(\((?[a-zA-Z0-9_]+)\))?/', $sub_table, $match);
+
+ if ($match[ 'join' ] !== '' && $match[ 'table' ] !== '')
+ {
+ if (is_string($relation))
+ {
+ $relation = 'USING ("' . $relation . '")';
+ }
+
+ if (is_array($relation))
+ {
+ // For ['column1', 'column2']
+ if (isset($relation[ 0 ]))
+ {
+ $relation = 'USING ("' . implode($relation, '", "') . '")';
+ }
+ else
+ {
+ $joins = [];
+
+ foreach ($relation as $key => $value)
+ {
+ $joins[] = (
+ strpos($key, '.') > 0 ?
+ // For ['tableB.column' => 'column']
+ $this->columnQuote($key) :
+
+ // For ['column1' => 'column2']
+ $table . '."' . $key . '"'
+ ) .
+ ' = ' .
+ $this->tableQuote(isset($match[ 'alias' ]) ? $match[ 'alias' ] : $match[ 'table' ]) . '."' . $value . '"';
+ }
+
+ $relation = 'ON ' . implode($joins, ' AND ');
+ }
+ }
+
+ $table_name = $this->tableQuote($match[ 'table' ]) . ' ';
+
+ if (isset($match[ 'alias' ]))
+ {
+ $table_name .= 'AS ' . $this->tableQuote($match[ 'alias' ]) . ' ';
+ }
+
+ $table_join[] = $join_array[ $match[ 'join' ] ] . ' JOIN ' . $table_name . $relation;
+ }
+ }
+
+ $table_query .= ' ' . implode($table_join, ' ');
+ }
+ else
+ {
+ if (is_null($columns))
+ {
+ if (
+ !is_null($where) ||
+ (is_array($join) && isset($column_fn))
+ )
+ {
+ $where = $join;
+ $columns = null;
+ }
+ else
+ {
+ $where = null;
+ $columns = $join;
+ }
+ }
+ else
+ {
+ $where = $columns;
+ $columns = $join;
+ }
+ }
+
+ if (isset($column_fn))
+ {
+ if ($column_fn === 1)
+ {
+ $column = '1';
+
+ if (is_null($where))
+ {
+ $where = $columns;
+ }
+ }
+ else
+ {
+ if (empty($columns) || $this->isRaw($columns))
+ {
+ $columns = '*';
+ $where = $join;
+ }
+
+ $column = $column_fn . '(' . $this->columnPush($columns, $map) . ')';
+ }
+ }
+ else
+ {
+ $column = $this->columnPush($columns, $map);
+ }
+
+ return 'SELECT ' . $column . ' FROM ' . $table_query . $this->whereClause($where, $map);
+ }
+
+ protected function columnMap($columns, &$stack)
+ {
+ if ($columns === '*')
+ {
+ return $stack;
+ }
+
+ foreach ($columns as $key => $value)
+ {
+ if (is_int($key))
+ {
+ preg_match('/([a-zA-Z0-9_]+\.)?(?[a-zA-Z0-9_]+)(?:\s*\((?[a-zA-Z0-9_]+)\))?(?:\s*\[(?(?:String|Bool|Int|Number|Object|JSON))\])?/i', $value, $key_match);
+
+ $column_key = !empty($key_match[ 'alias' ]) ?
+ $key_match[ 'alias' ] :
+ $key_match[ 'column' ];
+
+ if (isset($key_match[ 'type' ]))
+ {
+ $stack[ $value ] = [$column_key, $key_match[ 'type' ]];
+ }
+ else
+ {
+ $stack[ $value ] = [$column_key, 'String'];
+ }
+ }
+ elseif ($this->isRaw($value))
+ {
+ preg_match('/([a-zA-Z0-9_]+\.)?(?[a-zA-Z0-9_]+)(\s*\[(?(String|Bool|Int|Number))\])?/i', $key, $key_match);
+
+ $column_key = $key_match[ 'column' ];
+
+ if (isset($key_match[ 'type' ]))
+ {
+ $stack[ $key ] = [$column_key, $key_match[ 'type' ]];
+ }
+ else
+ {
+ $stack[ $key ] = [$column_key, 'String'];
+ }
+ }
+ elseif (!is_int($key) && is_array($value))
+ {
+ $this->columnMap($value, $stack);
+ }
+ }
+
+ return $stack;
+ }
+
+ protected function dataMap($data, $columns, $column_map, &$stack)
+ {
+ foreach ($columns as $key => $value)
+ {
+ $isRaw = $this->isRaw($value);
+
+ if (is_int($key) || $isRaw)
+ {
+ $map = $column_map[ $isRaw ? $key : $value ];
+
+ $column_key = $map[ 0 ];
+
+ $result = $data[ $column_key ];
+
+ if (isset($map[ 1 ]))
+ {
+ if ($isRaw && in_array($map[ 1 ], ['Object', 'JSON']))
+ {
+ continue;
+ }
+
+ if (is_null($result))
+ {
+ $stack[ $column_key ] = null;
+ continue;
+ }
+
+ switch ($map[ 1 ])
+ {
+ case 'Number':
+ $stack[ $column_key ] = (double) $result;
+ break;
+
+ case 'Int':
+ $stack[ $column_key ] = (int) $result;
+ break;
+
+ case 'Bool':
+ $stack[ $column_key ] = (bool) $result;
+ break;
+
+ case 'Object':
+ $stack[ $column_key ] = unserialize($result);
+ break;
+
+ case 'JSON':
+ $stack[ $column_key ] = json_decode($result, true);
+ break;
+
+ case 'String':
+ $stack[ $column_key ] = $result;
+ break;
+ }
+ }
+ else
+ {
+ $stack[ $column_key ] = $result;
+ }
+ }
+ else
+ {
+ $current_stack = [];
+
+ $this->dataMap($data, $value, $column_map, $current_stack);
+
+ $stack[ $key ] = $current_stack;
+ }
+ }
+ }
+
+ public function select($table, $join, $columns = null, $where = null)
+ {
+ $map = [];
+ $stack = [];
+ $column_map = [];
+
+ $index = 0;
+
+ $column = $where === null ? $join : $columns;
+
+ $is_single = (is_string($column) && $column !== '*');
+
+ $query = $this->exec($this->selectContext($table, $map, $join, $columns, $where), $map);
+
+ $this->columnMap($columns, $column_map);
+
+ if (!$query)
+ {
+ return false;
+ }
+
+ if ($columns === '*')
+ {
+ return $query->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ if ($is_single)
+ {
+ return $query->fetchAll(PDO::FETCH_COLUMN);
+ }
+
+ while ($data = $query->fetch(PDO::FETCH_ASSOC))
+ {
+ $current_stack = [];
+
+ $this->dataMap($data, $columns, $column_map, $current_stack);
+
+ $stack[ $index ] = $current_stack;
+
+ $index++;
+ }
+
+ return $stack;
+ }
+
+ public function insert($table, $datas)
+ {
+ $stack = [];
+ $columns = [];
+ $fields = [];
+ $map = [];
+
+ if (!isset($datas[ 0 ]))
+ {
+ $datas = [$datas];
+ }
+
+ foreach ($datas as $data)
+ {
+ foreach ($data as $key => $value)
+ {
+ $columns[] = $key;
+ }
+ }
+
+ $columns = array_unique($columns);
+
+ foreach ($datas as $data)
+ {
+ $values = [];
+
+ foreach ($columns as $key)
+ {
+ if ($raw = $this->buildRaw($data[ $key ], $map))
+ {
+ $values[] = $raw;
+ continue;
+ }
+
+ $map_key =$this->mapKey();
+
+ $values[] = $map_key;
+
+ if (!isset($data[ $key ]))
+ {
+ $map[ $map_key ] = [null, PDO::PARAM_NULL];
+ }
+ else
+ {
+ $value = $data[ $key ];
+
+ $type = gettype($value);
+
+ switch ($type)
+ {
+ case 'array':
+ $map[ $map_key ] = [
+ strpos($key, '[JSON]') === strlen($key) - 6 ?
+ json_encode($value) :
+ serialize($value),
+ PDO::PARAM_STR
+ ];
+ break;
+
+ case 'object':
+ $value = serialize($value);
+
+ case 'NULL':
+ case 'resource':
+ case 'boolean':
+ case 'integer':
+ case 'double':
+ case 'string':
+ $map[ $map_key ] = $this->typeMap($value, $type);
+ break;
+ }
+ }
+ }
+
+ $stack[] = '(' . implode($values, ', ') . ')';
+ }
+
+ foreach ($columns as $key)
+ {
+ $fields[] = $this->columnQuote(preg_replace("/(\s*\[JSON\]$)/i", '', $key));
+ }
+
+ return $this->exec('INSERT INTO ' . $this->tableQuote($table) . ' (' . implode(', ', $fields) . ') VALUES ' . implode(', ', $stack), $map);
+ }
+
+ public function update($table, $data, $where = null)
+ {
+ $fields = [];
+ $map = [];
+
+ foreach ($data as $key => $value)
+ {
+ $column = $this->columnQuote(preg_replace("/(\s*\[(JSON|\+|\-|\*|\/)\]$)/i", '', $key));
+
+ if ($raw = $this->buildRaw($value, $map))
+ {
+ $fields[] = $column . ' = ' . $raw;
+ continue;
+ }
+
+ $map_key = $this->mapKey();
+
+ preg_match('/(?[a-zA-Z0-9_]+)(\[(?\+|\-|\*|\/)\])?/i', $key, $match);
+
+ if (isset($match[ 'operator' ]))
+ {
+ if (is_numeric($value))
+ {
+ $fields[] = $column . ' = ' . $column . ' ' . $match[ 'operator' ] . ' ' . $value;
+ }
+ }
+ else
+ {
+ $fields[] = $column . ' = ' . $map_key;
+
+ $type = gettype($value);
+
+ switch ($type)
+ {
+ case 'array':
+ $map[ $map_key ] = [
+ strpos($key, '[JSON]') === strlen($key) - 6 ?
+ json_encode($value) :
+ serialize($value),
+ PDO::PARAM_STR
+ ];
+ break;
+
+ case 'object':
+ $value = serialize($value);
+
+ case 'NULL':
+ case 'resource':
+ case 'boolean':
+ case 'integer':
+ case 'double':
+ case 'string':
+ $map[ $map_key ] = $this->typeMap($value, $type);
+ break;
+ }
+ }
+ }
+
+ return $this->exec('UPDATE ' . $this->tableQuote($table) . ' SET ' . implode(', ', $fields) . $this->whereClause($where, $map), $map);
+ }
+
+ public function delete($table, $where)
+ {
+ $map = [];
+
+ return $this->exec('DELETE FROM ' . $this->tableQuote($table) . $this->whereClause($where, $map), $map);
+ }
+
+ public function replace($table, $columns, $where = null)
+ {
+ if (!is_array($columns) || empty($columns))
+ {
+ return false;
+ }
+
+ $map = [];
+ $stack = [];
+
+ foreach ($columns as $column => $replacements)
+ {
+ if (is_array($replacements))
+ {
+ foreach ($replacements as $old => $new)
+ {
+ $map_key = $this->mapKey();
+
+ $stack[] = $this->columnQuote($column) . ' = REPLACE(' . $this->columnQuote($column) . ', ' . $map_key . 'a, ' . $map_key . 'b)';
+
+ $map[ $map_key . 'a' ] = [$old, PDO::PARAM_STR];
+ $map[ $map_key . 'b' ] = [$new, PDO::PARAM_STR];
+ }
+ }
+ }
+
+ if (!empty($stack))
+ {
+ return $this->exec('UPDATE ' . $this->tableQuote($table) . ' SET ' . implode(', ', $stack) . $this->whereClause($where, $map), $map);
+ }
+
+ return false;
+ }
+
+ public function get($table, $join = null, $columns = null, $where = null)
+ {
+ $map = [];
+ $stack = [];
+ $column_map = [];
+
+ if ($where === null)
+ {
+ $column = $join;
+ unset($columns[ 'LIMIT' ]);
+ }
+ else
+ {
+ $column = $columns;
+ unset($where[ 'LIMIT' ]);
+ }
+
+ $is_single = (is_string($column) && $column !== '*');
+
+ $query = $this->exec($this->selectContext($table, $map, $join, $columns, $where) . ' LIMIT 1', $map);
+
+ if ($query)
+ {
+ $data = $query->fetchAll(PDO::FETCH_ASSOC);
+
+ if (isset($data[ 0 ]))
+ {
+ if ($column === '*')
+ {
+ return $data[ 0 ];
+ }
+
+ $this->columnMap($columns, $column_map);
+
+ $this->dataMap($data[ 0 ], $columns, $column_map, $stack);
+
+ if ($is_single)
+ {
+ return $stack[ $column_map[ $column ][ 0 ] ];
+ }
+
+ return $stack;
+ }
+
+ return false;
+ }
+
+ return false;
+ }
+
+ public function has($table, $join, $where = null)
+ {
+ $map = [];
+ $column = null;
+
+ $query = $this->exec('SELECT EXISTS(' . $this->selectContext($table, $map, $join, $column, $where, 1) . ')', $map);
+
+ if ($query)
+ {
+ $result = $query->fetchColumn();
+
+ return $result === '1' || $result === true;
+ }
+
+ return false;
+ }
+
+ private function aggregate($type, $table, $join = null, $column = null, $where = null)
+ {
+ $map = [];
+
+ $query = $this->exec($this->selectContext($table, $map, $join, $column, $where, strtoupper($type)), $map);
+
+ if ($query)
+ {
+ $number = $query->fetchColumn();
+
+ return is_numeric($number) ? $number + 0 : $number;
+ }
+
+ return false;
+ }
+
+ public function action($actions)
+ {
+ if (is_callable($actions))
+ {
+ $this->pdo->beginTransaction();
+
+ try {
+ $result = $actions($this);
+
+ if ($result === false)
+ {
+ $this->pdo->rollBack();
+ }
+ else
+ {
+ $this->pdo->commit();
+ }
+ }
+ catch (Exception $e) {
+ $this->pdo->rollBack();
+
+ throw $e;
+ }
+
+ return $result;
+ }
+
+ return false;
+ }
+
+ public function id()
+ {
+ $type = $this->type;
+
+ if ($type === 'oracle')
+ {
+ return 0;
+ }
+ elseif ($type === 'mssql')
+ {
+ return $this->pdo->query('SELECT SCOPE_IDENTITY()')->fetchColumn();
+ }
+ elseif ($type === 'pgsql')
+ {
+ return $this->pdo->query('SELECT LASTVAL()')->fetchColumn();
+ }
+
+ return $this->pdo->lastInsertId();
+ }
+
+ public function debug()
+ {
+ $this->debug_mode = true;
+
+ return $this;
+ }
+
+ public function error()
+ {
+ return $this->statement ? $this->statement->errorInfo() : null;
+ }
+
+ public function last()
+ {
+ $log = end($this->logs);
+
+ return $this->generate($log[ 0 ], $log[ 1 ]);
+ }
+
+ public function log()
+ {
+ return array_map(function ($log)
+ {
+ return $this->generate($log[ 0 ], $log[ 1 ]);
+ },
+ $this->logs
+ );
+ }
+
+ public function info()
+ {
+ $output = [
+ 'server' => 'SERVER_INFO',
+ 'driver' => 'DRIVER_NAME',
+ 'client' => 'CLIENT_VERSION',
+ 'version' => 'SERVER_VERSION',
+ 'connection' => 'CONNECTION_STATUS'
+ ];
+
+ foreach ($output as $key => $value)
+ {
+ $output[ $key ] = @$this->pdo->getAttribute(constant('PDO::ATTR_' . $value));
+ }
+
+ return $output;
+ }
+}
+?>
\ No newline at end of file
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
index 720e426f..9bd88669 100644
--- a/vendor/composer/autoload_psr4.php
+++ b/vendor/composer/autoload_psr4.php
@@ -15,6 +15,7 @@ return array(
'PhpExtended\\Tail\\' => array($vendorDir . '/php-extended/php-tail/src'),
'PhpExtended\\System\\' => array($vendorDir . '/php-extended/php-system/src'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
+ 'Medoo\\' => array($vendorDir . '/catfan/medoo/src'),
'League\\Plates\\' => array($vendorDir . '/league/plates/src'),
'Leafo\\ScssPhp\\' => array($vendorDir . '/leafo/scssphp/src'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 1762a4fd..85e66636 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -42,6 +42,7 @@ class ComposerStaticInit67b09c83b85c3bc0027caefe5bba171a
'M' =>
array (
'Monolog\\' => 8,
+ 'Medoo\\' => 6,
),
'L' =>
array (
@@ -93,6 +94,10 @@ class ComposerStaticInit67b09c83b85c3bc0027caefe5bba171a
array (
0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog',
),
+ 'Medoo\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/catfan/medoo/src',
+ ),
'League\\Plates\\' =>
array (
0 => __DIR__ . '/..' . '/league/plates/src',
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 1829b0a3..7b29a9ee 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -47,6 +47,67 @@
"utility"
]
},
+ {
+ "name": "catfan/medoo",
+ "version": "v1.5.7",
+ "version_normalized": "1.5.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/catfan/Medoo.git",
+ "reference": "8d90cba0e8ff176028847527d0ea76fe41a06ecf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/catfan/Medoo/zipball/8d90cba0e8ff176028847527d0ea76fe41a06ecf",
+ "reference": "8d90cba0e8ff176028847527d0ea76fe41a06ecf",
+ "shasum": ""
+ },
+ "require": {
+ "ext-pdo": "*",
+ "php": ">=5.4"
+ },
+ "suggest": {
+ "ext-pdo_dblib": "For MSSQL or Sybase database on Linux/UNIX platform",
+ "ext-pdo_mysql": "For MySQL or MariaDB database",
+ "ext-pdo_oci": "For Oracle database",
+ "ext-pdo_oci8": "For Oracle version 8 database",
+ "ext-pdo_pqsql": "For PostgreSQL database",
+ "ext-pdo_sqlite": "For SQLite database",
+ "ext-pdo_sqlsrv": "For MSSQL database"
+ },
+ "time": "2018-06-14T18:59:08+00:00",
+ "type": "framework",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Medoo\\": "/src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Angel Lai",
+ "email": "angel@catfan.me"
+ }
+ ],
+ "description": "The lightest PHP database framework to accelerate development",
+ "homepage": "https://medoo.in",
+ "keywords": [
+ "database",
+ "lightweight",
+ "mariadb",
+ "mssql",
+ "mysql",
+ "oracle",
+ "php framework",
+ "postgresql",
+ "sql",
+ "sqlite"
+ ]
+ },
{
"name": "ezyang/htmlpurifier",
"version": "v4.10.0",
@@ -323,17 +384,17 @@
},
{
"name": "leafo/scssphp",
- "version": "v0.7.6",
- "version_normalized": "0.7.6.0",
+ "version": "v0.7.7",
+ "version_normalized": "0.7.7.0",
"source": {
"type": "git",
"url": "https://github.com/leafo/scssphp.git",
- "reference": "585f6ae84de62ffecf69c23805f25d78d7e4b794"
+ "reference": "1d656f8c02a3a69404bba6b28ec4e06edddf0f49"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/leafo/scssphp/zipball/585f6ae84de62ffecf69c23805f25d78d7e4b794",
- "reference": "585f6ae84de62ffecf69c23805f25d78d7e4b794",
+ "url": "https://api.github.com/repos/leafo/scssphp/zipball/1d656f8c02a3a69404bba6b28ec4e06edddf0f49",
+ "reference": "1d656f8c02a3a69404bba6b28ec4e06edddf0f49",
"shasum": ""
},
"require": {
@@ -343,7 +404,7 @@
"phpunit/phpunit": "~4.6",
"squizlabs/php_codesniffer": "~2.5"
},
- "time": "2018-05-24T02:18:53+00:00",
+ "time": "2018-07-22T01:22:08+00:00",
"bin": [
"bin/pscss"
],
@@ -774,17 +835,17 @@
},
{
"name": "symfony/lock",
- "version": "v4.1.0",
- "version_normalized": "4.1.0.0",
+ "version": "v4.1.1",
+ "version_normalized": "4.1.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/lock.git",
- "reference": "a1df50492e927ad00ca271f29adb2949a14f2a5a"
+ "reference": "d508b5dd92272cb74300396ecaadde969d0f7976"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/lock/zipball/a1df50492e927ad00ca271f29adb2949a14f2a5a",
- "reference": "a1df50492e927ad00ca271f29adb2949a14f2a5a",
+ "url": "https://api.github.com/repos/symfony/lock/zipball/d508b5dd92272cb74300396ecaadde969d0f7976",
+ "reference": "d508b5dd92272cb74300396ecaadde969d0f7976",
"shasum": ""
},
"require": {
@@ -794,7 +855,7 @@
"require-dev": {
"predis/predis": "~1.0"
},
- "time": "2018-05-30T07:26:09+00:00",
+ "time": "2018-06-21T11:15:46+00:00",
"type": "library",
"extra": {
"branch-alias": {
diff --git a/vendor/leafo/scssphp/src/Compiler.php b/vendor/leafo/scssphp/src/Compiler.php
index 72df991e..637f1c1c 100644
--- a/vendor/leafo/scssphp/src/Compiler.php
+++ b/vendor/leafo/scssphp/src/Compiler.php
@@ -3655,7 +3655,7 @@ class Compiler
return false;
}
- list($sorted, $kwargs) = $this->sortArgs($prototype, $args);
+ @list($sorted, $kwargs) = $this->sortArgs($prototype, $args);
if ($name !== 'if' && $name !== 'call') {
foreach ($sorted as &$val) {
@@ -3714,7 +3714,7 @@ class Compiler
$key = $key[1];
if (empty($key)) {
- $posArgs[] = $value;
+ $posArgs[] = empty($arg[2]) ? $value : $arg;
} else {
$keyArgs[$key] = $value;
}
@@ -4266,20 +4266,38 @@ class Compiler
{
$name = $this->compileStringContent($this->coerceString($this->reduce(array_shift($args), true)));
- $args = array_map(
- function ($a) {
- return [null, $a, false];
- },
- $args
- );
+ $posArgs = [];
+
+ foreach ($args as $arg) {
+ if (empty($arg[0])) {
+ if ($arg[2] === true) {
+ $tmp = $this->reduce($arg[1]);
+
+ if ($tmp[0] === Type::T_LIST) {
+ foreach ($tmp[2] as $item) {
+ $posArgs[] = [null, $item, false];
+ }
+ } else {
+ $posArgs[] = [null, $tmp, true];
+ }
+
+ continue;
+ }
+
+ $posArgs[] = [null, $this->reduce($arg), false];
+ continue;
+ }
+
+ $posArgs[] = [null, $arg, false];
+ }
if (count($kwargs)) {
foreach ($kwargs as $key => $value) {
- $args[] = [[Type::T_VARIABLE, $key], $value, false];
+ $posArgs[] = [[Type::T_VARIABLE, $key], $value, false];
}
}
- return $this->reduce([Type::T_FUNCTION_CALL, $name, $args]);
+ return $this->reduce([Type::T_FUNCTION_CALL, $name, $posArgs]);
}
protected static $libIf = ['condition', 'if-true', 'if-false'];
@@ -4838,7 +4856,7 @@ class Compiler
if (null === $unit) {
$unit = $number[2];
$originalUnit = $item->unitStr();
- } elseif ($unit !== $number[2]) {
+ } elseif ($number[1] && $unit !== $number[2]) {
$this->throwError('Incompatible units: "%s" and "%s".', $originalUnit, $item->unitStr());
break;
}
@@ -4985,7 +5003,21 @@ class Compiler
$map1 = $this->assertMap($args[0]);
$map2 = $this->assertMap($args[1]);
- return [Type::T_MAP, array_merge($map1[1], $map2[1]), array_merge($map1[2], $map2[2])];
+ foreach ($map2[1] as $i2 => $key2) {
+ $key = $this->compileStringContent($this->coerceString($key2));
+
+ foreach ($map1[1] as $i1 => $key1) {
+ if ($key === $this->compileStringContent($this->coerceString($key1))) {
+ $map1[2][$i1] = $map2[2][$i2];
+ continue 2;
+ }
+ }
+
+ $map1[1][] = $map2[1][$i2];
+ $map1[2][] = $map2[2][$i2];
+ }
+
+ return $map1;
}
protected static $libKeywords = ['args'];
diff --git a/vendor/symfony/lock/Store/FlockStore.php b/vendor/symfony/lock/Store/FlockStore.php
index bf72eaef..1ef9ec84 100644
--- a/vendor/symfony/lock/Store/FlockStore.php
+++ b/vendor/symfony/lock/Store/FlockStore.php
@@ -79,12 +79,12 @@ class FlockStore implements StoreInterface
// Silence error reporting
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
- if (!$handle = fopen($fileName, 'r')) {
+ if (!$handle = fopen($fileName, 'r+') ?: fopen($fileName, 'r')) {
if ($handle = fopen($fileName, 'x')) {
chmod($fileName, 0444);
- } elseif (!$handle = fopen($fileName, 'r')) {
+ } elseif (!$handle = fopen($fileName, 'r+') ?: fopen($fileName, 'r')) {
usleep(100); // Give some time for chmod() to complete
- $handle = fopen($fileName, 'r');
+ $handle = fopen($fileName, 'r+') ?: fopen($fileName, 'r');
}
}
restore_error_handler();
diff --git a/vendor/symfony/lock/Store/SemaphoreStore.php b/vendor/symfony/lock/Store/SemaphoreStore.php
index 8913e391..6a32a303 100644
--- a/vendor/symfony/lock/Store/SemaphoreStore.php
+++ b/vendor/symfony/lock/Store/SemaphoreStore.php
@@ -64,8 +64,14 @@ class SemaphoreStore implements StoreInterface
return;
}
- $resource = sem_get(crc32($key));
- $acquired = sem_acquire($resource, !$blocking);
+ $keyId = crc32($key);
+ $resource = sem_get($keyId);
+ $acquired = @sem_acquire($resource, !$blocking);
+
+ while ($blocking && !$acquired) {
+ $resource = sem_get($keyId);
+ $acquired = @sem_acquire($resource);
+ }
if (!$acquired) {
throw new LockConflictedException();
@@ -86,7 +92,7 @@ class SemaphoreStore implements StoreInterface
$resource = $key->getState(__CLASS__);
- sem_release($resource);
+ sem_remove($resource);
$key->removeState(__CLASS__);
}
diff --git a/vendor/symfony/lock/Tests/Store/SemaphoreStoreTest.php b/vendor/symfony/lock/Tests/Store/SemaphoreStoreTest.php
index eeb95d58..97c93c78 100644
--- a/vendor/symfony/lock/Tests/Store/SemaphoreStoreTest.php
+++ b/vendor/symfony/lock/Tests/Store/SemaphoreStoreTest.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Lock\Tests\Store;
+use Symfony\Component\Lock\Key;
use Symfony\Component\Lock\Store\SemaphoreStore;
/**
@@ -29,4 +30,31 @@ class SemaphoreStoreTest extends AbstractStoreTest
{
return new SemaphoreStore();
}
+
+ public function testResourceRemoval()
+ {
+ $initialCount = $this->getOpenedSemaphores();
+ $store = new SemaphoreStore();
+ $key = new Key(uniqid(__METHOD__, true));
+ $store->waitAndSave($key);
+
+ $this->assertGreaterThan($initialCount, $this->getOpenedSemaphores(), 'Semaphores should have been created');
+
+ $store->delete($key);
+ $this->assertEquals($initialCount, $this->getOpenedSemaphores(), 'All semaphores should be removed');
+ }
+
+ private function getOpenedSemaphores()
+ {
+ $lines = explode(PHP_EOL, trim(`ipcs -su`));
+ if ('------ Semaphore Status --------' !== $lines[0]) {
+ throw new \Exception('Failed to extract list of opend semaphores. Expect a Semaphore status, got '.implode(PHP_EOL, $lines));
+ }
+ list($key, $value) = explode(' = ', $lines[1]);
+ if ('used arrays' !== $key) {
+ throw new \Exception('Failed to extract list of opend semaphores. Expect a used arrays key, got '.implode(PHP_EOL, $lines));
+ }
+
+ return (int) $value;
+ }
}