First Local Commit - After Clean up.
Signed-off-by: Rick Hays <rhays@haysgang.com>
This commit is contained in:
6
system/.htaccess
Normal file
6
system/.htaccess
Normal file
@@ -0,0 +1,6 @@
|
||||
<IfModule authz_core_module>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !authz_core_module>
|
||||
Deny from all
|
||||
</IfModule>
|
||||
390
system/API/ResponseTrait.php
Normal file
390
system/API/ResponseTrait.php
Normal file
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\API;
|
||||
|
||||
use Config\Format;
|
||||
use CodeIgniter\HTTP\Response;
|
||||
|
||||
/**
|
||||
* Response trait.
|
||||
*
|
||||
* Provides common, more readable, methods to provide
|
||||
* consistent HTTP responses under a variety of common
|
||||
* situations when working as an API.
|
||||
*
|
||||
* @property \CodeIgniter\HTTP\IncomingRequest $request
|
||||
* @property \CodeIgniter\HTTP\Response $response
|
||||
*
|
||||
* @package CodeIgniter\API
|
||||
*/
|
||||
trait ResponseTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* Allows child classes to override the
|
||||
* status code that is used in their API.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $codes = [
|
||||
'created' => 201,
|
||||
'deleted' => 200,
|
||||
'no_content' => 204,
|
||||
'invalid_request' => 400,
|
||||
'unsupported_response_type' => 400,
|
||||
'invalid_scope' => 400,
|
||||
'temporarily_unavailable' => 400,
|
||||
'invalid_grant' => 400,
|
||||
'invalid_credentials' => 400,
|
||||
'invalid_refresh' => 400,
|
||||
'no_data' => 400,
|
||||
'invalid_data' => 400,
|
||||
'access_denied' => 401,
|
||||
'unauthorized' => 401,
|
||||
'invalid_client' => 401,
|
||||
'forbidden' => 403,
|
||||
'resource_not_found' => 404,
|
||||
'not_acceptable' => 406,
|
||||
'resource_exists' => 409,
|
||||
'conflict' => 409,
|
||||
'resource_gone' => 410,
|
||||
'payload_too_large' => 413,
|
||||
'unsupported_media_type' => 415,
|
||||
'too_many_requests' => 429,
|
||||
'server_error' => 500,
|
||||
'unsupported_grant_type' => 501,
|
||||
'not_implemented' => 501,
|
||||
];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Provides a single, simple method to return an API response, formatted
|
||||
* to match the requested format, with proper content-type and status code.
|
||||
*
|
||||
* @param array|string|null $data
|
||||
* @param integer $status
|
||||
* @param string $message
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function respond($data = null, int $status = null, string $message = '')
|
||||
{
|
||||
// If data is null and status code not provided, exit and bail
|
||||
if ($data === null && $status === null)
|
||||
{
|
||||
$status = 404;
|
||||
|
||||
// Create the output var here in case of $this->response([]);
|
||||
$output = null;
|
||||
} // If data is null but status provided, keep the output empty.
|
||||
elseif ($data === null && is_numeric($status))
|
||||
{
|
||||
$output = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$status = empty($status) ? 200 : $status;
|
||||
$output = $this->format($data);
|
||||
}
|
||||
|
||||
return $this->response->setBody($output)
|
||||
->setStatusCode($status, $message);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Used for generic failures that no custom methods exist for.
|
||||
*
|
||||
* @param string|array $messages
|
||||
* @param integer|null $status HTTP status code
|
||||
* @param string|null $code Custom, API-specific, error code
|
||||
* @param string $customMessage
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fail($messages, int $status = 400, string $code = null, string $customMessage = '')
|
||||
{
|
||||
if (! is_array($messages))
|
||||
{
|
||||
$messages = ['error' => $messages];
|
||||
}
|
||||
|
||||
$response = [
|
||||
'status' => $status,
|
||||
'error' => $code === null ? $status : $code,
|
||||
'messages' => $messages,
|
||||
];
|
||||
|
||||
return $this->respond($response, $status, $customMessage);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------
|
||||
// Response Helpers
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Used after successfully creating a new resource.
|
||||
*
|
||||
* @param mixed $data Data.
|
||||
* @param string $message Message.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function respondCreated($data = null, string $message = '')
|
||||
{
|
||||
return $this->respond($data, $this->codes['created'], $message);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Used after a resource has been successfully deleted.
|
||||
*
|
||||
* @param mixed $data Data.
|
||||
* @param string $message Message.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function respondDeleted($data = null, string $message = '')
|
||||
{
|
||||
return $this->respond($data, $this->codes['deleted'], $message);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Used after a command has been successfully executed but there is no
|
||||
* meaningful reply to send back to the client.
|
||||
*
|
||||
* @param string $message Message.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function respondNoContent(string $message = 'No Content')
|
||||
{
|
||||
return $this->respond(null, $this->codes['no_content'], $message);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Used when the client is either didn't send authorization information,
|
||||
* or had bad authorization credentials. User is encouraged to try again
|
||||
* with the proper information.
|
||||
*
|
||||
* @param string $description
|
||||
* @param string $code
|
||||
* @param string $message
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function failUnauthorized(string $description = 'Unauthorized', string $code = null, string $message = '')
|
||||
{
|
||||
return $this->fail($description, $this->codes['unauthorized'], $code, $message);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Used when access is always denied to this resource and no amount
|
||||
* of trying again will help.
|
||||
*
|
||||
* @param string $description
|
||||
* @param string $code
|
||||
* @param string $message
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function failForbidden(string $description = 'Forbidden', string $code = null, string $message = '')
|
||||
{
|
||||
return $this->fail($description, $this->codes['forbidden'], $code, $message);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Used when a specified resource cannot be found.
|
||||
*
|
||||
* @param string $description
|
||||
* @param string $code
|
||||
* @param string $message
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function failNotFound(string $description = 'Not Found', string $code = null, string $message = '')
|
||||
{
|
||||
return $this->fail($description, $this->codes['resource_not_found'], $code, $message);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Used when the data provided by the client cannot be validated.
|
||||
*
|
||||
* @param string $description
|
||||
* @param string $code
|
||||
* @param string $message
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function failValidationError(string $description = 'Bad Request', string $code = null, string $message = '')
|
||||
{
|
||||
return $this->fail($description, $this->codes['invalid_data'], $code, $message);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Use when trying to create a new resource and it already exists.
|
||||
*
|
||||
* @param string $description
|
||||
* @param string $code
|
||||
* @param string $message
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function failResourceExists(string $description = 'Conflict', string $code = null, string $message = '')
|
||||
{
|
||||
return $this->fail($description, $this->codes['resource_exists'], $code, $message);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Use when a resource was previously deleted. This is different than
|
||||
* Not Found, because here we know the data previously existed, but is now gone,
|
||||
* where Not Found means we simply cannot find any information about it.
|
||||
*
|
||||
* @param string $description
|
||||
* @param string $code
|
||||
* @param string $message
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function failResourceGone(string $description = 'Gone', string $code = null, string $message = '')
|
||||
{
|
||||
return $this->fail($description, $this->codes['resource_gone'], $code, $message);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Used when the user has made too many requests for the resource recently.
|
||||
*
|
||||
* @param string $description
|
||||
* @param string $code
|
||||
* @param string $message
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function failTooManyRequests(string $description = 'Too Many Requests', string $code = null, string $message = '')
|
||||
{
|
||||
return $this->fail($description, $this->codes['too_many_requests'], $code, $message);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Used when there is a server error.
|
||||
*
|
||||
* @param string $description The error message to show the user.
|
||||
* @param string|null $code A custom, API-specific, error code.
|
||||
* @param string $message A custom "reason" message to return.
|
||||
*
|
||||
* @return Response The value of the Response's send() method.
|
||||
*/
|
||||
public function failServerError(string $description = 'Internal Server Error', string $code = null, string $message = ''): Response
|
||||
{
|
||||
return $this->fail($description, $this->codes['server_error'], $code, $message);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Utility Methods
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Handles formatting a response. Currently makes some heavy assumptions
|
||||
* and needs updating! :)
|
||||
*
|
||||
* @param string|array|null $data
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function format($data = null)
|
||||
{
|
||||
// If the data is a string, there's not much we can do to it...
|
||||
if (is_string($data))
|
||||
{
|
||||
// The content type should be text/... and not application/...
|
||||
$contentType = $this->response->getHeaderLine('Content-Type');
|
||||
$contentType = str_replace('application/json', 'text/html', $contentType);
|
||||
$contentType = str_replace('application/', 'text/', $contentType);
|
||||
$this->response->setContentType($contentType);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
// Determine correct response type through content negotiation
|
||||
$config = new Format();
|
||||
$format = $this->request->negotiate('media', $config->supportedResponseFormats, false);
|
||||
|
||||
$this->response->setContentType($format);
|
||||
|
||||
// if we don't have a formatter, make one
|
||||
if (! isset($this->formatter))
|
||||
{
|
||||
// if no formatter, use the default
|
||||
$this->formatter = $config->getFormatter($format);
|
||||
}
|
||||
|
||||
if ($format !== 'application/json')
|
||||
{
|
||||
// Recursively convert objects into associative arrays
|
||||
// Conversion not required for JSONFormatter
|
||||
$data = json_decode(json_encode($data), true);
|
||||
}
|
||||
|
||||
return $this->formatter->format($data);
|
||||
}
|
||||
|
||||
}
|
||||
435
system/Autoloader/Autoloader.php
Normal file
435
system/Autoloader/Autoloader.php
Normal file
@@ -0,0 +1,435 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Autoloader;
|
||||
|
||||
/**
|
||||
* CodeIgniter Autoloader
|
||||
*
|
||||
* An autoloader that uses both PSR4 autoloading, and traditional classmaps.
|
||||
*
|
||||
* Given a foo-bar package of classes in the file system at the following paths:
|
||||
*
|
||||
* /path/to/packages/foo-bar/
|
||||
* /src
|
||||
* Baz.php # Foo\Bar\Baz
|
||||
* Qux/
|
||||
* Quux.php # Foo\Bar\Qux\Quux
|
||||
*
|
||||
* you can add the path to the configuration array that is passed in the constructor.
|
||||
* The Config array consists of 2 primary keys, both of which are associative arrays:
|
||||
* 'psr4', and 'classmap'.
|
||||
*
|
||||
* $Config = [
|
||||
* 'psr4' => [
|
||||
* 'Foo\Bar' => '/path/to/packages/foo-bar'
|
||||
* ],
|
||||
* 'classmap' => [
|
||||
* 'MyClass' => '/path/to/class/file.php'
|
||||
* ]
|
||||
* ];
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* <?php
|
||||
* // our configuration array
|
||||
* $Config = [ ... ];
|
||||
* $loader = new \CodeIgniter\Autoloader\Autoloader($Config);
|
||||
*
|
||||
* // register the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* @package CodeIgniter\Autoloader
|
||||
*/
|
||||
class Autoloader
|
||||
{
|
||||
/**
|
||||
* Stores namespaces as key, and path as values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $prefixes = [];
|
||||
|
||||
/**
|
||||
* Stores class name as key, and path as values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $classmap = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Reads in the configuration array (described above) and stores
|
||||
* the valid parts that we'll need.
|
||||
*
|
||||
* @param \Config\Autoload $config
|
||||
* @param \Config\Modules $moduleConfig
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function initialize(\Config\Autoload $config, \Config\Modules $moduleConfig)
|
||||
{
|
||||
// We have to have one or the other, though we don't enforce the need
|
||||
// to have both present in order to work.
|
||||
if (empty($config->psr4) && empty($config->classmap))
|
||||
{
|
||||
throw new \InvalidArgumentException('Config array must contain either the \'psr4\' key or the \'classmap\' key.');
|
||||
}
|
||||
|
||||
if (isset($config->psr4))
|
||||
{
|
||||
$this->addNamespace($config->psr4);
|
||||
}
|
||||
|
||||
if (isset($config->classmap))
|
||||
{
|
||||
$this->classmap = $config->classmap;
|
||||
}
|
||||
|
||||
// Should we load through Composer's namespaces, also?
|
||||
if ($moduleConfig->discoverInComposer)
|
||||
{
|
||||
$this->discoverComposerNamespaces();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Register the loader with the SPL autoloader stack.
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Since the default file extensions are searched
|
||||
// in order of .inc then .php, but we always use .php,
|
||||
// put the .php extension first to eek out a bit
|
||||
// better performance.
|
||||
// http://php.net/manual/en/function.spl-autoload.php#78053
|
||||
spl_autoload_extensions('.php,.inc');
|
||||
|
||||
// Prepend the PSR4 autoloader for maximum performance.
|
||||
spl_autoload_register([$this, 'loadClass'], true, true);
|
||||
|
||||
// Now prepend another loader for the files in our class map.
|
||||
$config = is_array($this->classmap) ? $this->classmap : [];
|
||||
|
||||
spl_autoload_register(function ($class) use ($config) {
|
||||
if (empty($config[$class]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
include_once $config[$class];
|
||||
}, true, // Throw exception
|
||||
true // Prepend
|
||||
);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Registers namespaces with the autoloader.
|
||||
*
|
||||
* @param array|string $namespace
|
||||
* @param string $path
|
||||
*
|
||||
* @return Autoloader
|
||||
*/
|
||||
public function addNamespace($namespace, string $path = null)
|
||||
{
|
||||
if (is_array($namespace))
|
||||
{
|
||||
foreach ($namespace as $prefix => $path)
|
||||
{
|
||||
$prefix = trim($prefix, '\\');
|
||||
|
||||
if (is_array($path))
|
||||
{
|
||||
foreach ($path as $dir)
|
||||
{
|
||||
$this->prefixes[$prefix][] = rtrim($dir, '/') . '/';
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->prefixes[$prefix][] = rtrim($path, '/') . '/';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->prefixes[trim($namespace, '\\')][] = rtrim($path, '/') . '/';
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get namespaces with prefixes as keys and paths as values.
|
||||
*
|
||||
* If a prefix param is set, returns only paths to the given prefix.
|
||||
*
|
||||
* @var string|null $prefix
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNamespace(string $prefix = null)
|
||||
{
|
||||
if ($prefix === null)
|
||||
{
|
||||
return $this->prefixes;
|
||||
}
|
||||
|
||||
return $this->prefixes[trim($prefix, '\\')] ?? [];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Removes a single namespace from the psr4 settings.
|
||||
*
|
||||
* @param string $namespace
|
||||
*
|
||||
* @return Autoloader
|
||||
*/
|
||||
public function removeNamespace(string $namespace)
|
||||
{
|
||||
unset($this->prefixes[trim($namespace, '\\')]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Loads the class file for a given class name.
|
||||
*
|
||||
* @param string $class The fully qualified class name.
|
||||
*
|
||||
* @return string|false The mapped file on success, or boolean false
|
||||
* on failure.
|
||||
*/
|
||||
public function loadClass(string $class)
|
||||
{
|
||||
$class = trim($class, '\\');
|
||||
$class = str_ireplace('.php', '', $class);
|
||||
|
||||
$mapped_file = $this->loadInNamespace($class);
|
||||
|
||||
// Nothing? One last chance by looking
|
||||
// in common CodeIgniter folders.
|
||||
if (! $mapped_file)
|
||||
{
|
||||
$mapped_file = $this->loadLegacy($class);
|
||||
}
|
||||
|
||||
return $mapped_file;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Loads the class file for a given class name.
|
||||
*
|
||||
* @param string $class The fully-qualified class name
|
||||
*
|
||||
* @return string|false The mapped file name on success, or boolean false on fail
|
||||
*/
|
||||
protected function loadInNamespace(string $class)
|
||||
{
|
||||
if (strpos($class, '\\') === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->prefixes as $namespace => $directories)
|
||||
{
|
||||
foreach ($directories as $directory)
|
||||
{
|
||||
$directory = rtrim($directory, '/');
|
||||
|
||||
if (strpos($class, $namespace) === 0)
|
||||
{
|
||||
$filePath = $directory . str_replace('\\', '/',
|
||||
substr($class, strlen($namespace))) . '.php';
|
||||
$filename = $this->requireFile($filePath);
|
||||
|
||||
if ($filename)
|
||||
{
|
||||
return $filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// never found a mapped file
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempts to load the class from common locations in previous
|
||||
* version of CodeIgniter, namely 'app/Libraries', and
|
||||
* 'app/Models'.
|
||||
*
|
||||
* @param string $class The class name. This typically should NOT have a namespace.
|
||||
*
|
||||
* @return mixed The mapped file name on success, or boolean false on failure
|
||||
*/
|
||||
protected function loadLegacy(string $class)
|
||||
{
|
||||
// If there is a namespace on this class, then
|
||||
// we cannot load it from traditional locations.
|
||||
if (strpos($class, '\\') !== false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$paths = [
|
||||
APPPATH . 'Controllers/',
|
||||
APPPATH . 'Libraries/',
|
||||
APPPATH . 'Models/',
|
||||
];
|
||||
|
||||
$class = str_replace('\\', '/', $class) . '.php';
|
||||
|
||||
foreach ($paths as $path)
|
||||
{
|
||||
if ($file = $this->requireFile($path . $class))
|
||||
{
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A central way to require a file is loaded. Split out primarily
|
||||
* for testing purposes.
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @return string|false The filename on success, false if the file is not loaded
|
||||
*/
|
||||
protected function requireFile(string $file)
|
||||
{
|
||||
$file = $this->sanitizeFilename($file);
|
||||
|
||||
if (is_file($file))
|
||||
{
|
||||
require_once $file;
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sanitizes a filename, replacing spaces with dashes.
|
||||
*
|
||||
* Removes special characters that are illegal in filenames on certain
|
||||
* operating systems and special characters requiring special escaping
|
||||
* to manipulate at the command line. Replaces spaces and consecutive
|
||||
* dashes with a single dash. Trim period, dash and underscore from beginning
|
||||
* and end of filename.
|
||||
*
|
||||
* @param string $filename
|
||||
*
|
||||
* @return string The sanitized filename
|
||||
*/
|
||||
public function sanitizeFilename(string $filename): string
|
||||
{
|
||||
// Only allow characters deemed safe for POSIX portable filenames.
|
||||
// Plus the forward slash for directory separators since this might
|
||||
// be a path.
|
||||
// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_278
|
||||
// Modified to allow backslash and colons for on Windows machines.
|
||||
$filename = preg_replace('/[^a-zA-Z0-9\s\/\-\_\.\:\\\\]/', '', $filename);
|
||||
|
||||
// Clean up our filename edges.
|
||||
$filename = trim($filename, '.-_');
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Locates all PSR4 compatible namespaces from Composer.
|
||||
*/
|
||||
protected function discoverComposerNamespaces()
|
||||
{
|
||||
if (! is_file(COMPOSER_PATH))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$composer = include COMPOSER_PATH;
|
||||
|
||||
$paths = $composer->getPrefixesPsr4();
|
||||
unset($composer);
|
||||
|
||||
// Get rid of CodeIgniter so we don't have duplicates
|
||||
if (isset($paths['CodeIgniter\\']))
|
||||
{
|
||||
unset($paths['CodeIgniter\\']);
|
||||
}
|
||||
|
||||
// Composer stores namespaces with trailing slash. We don't.
|
||||
$newPaths = [];
|
||||
foreach ($paths as $key => $value)
|
||||
{
|
||||
$newPaths[rtrim($key, '\\ ')] = $value;
|
||||
}
|
||||
|
||||
$this->prefixes = array_merge($this->prefixes, $newPaths);
|
||||
}
|
||||
}
|
||||
460
system/Autoloader/FileLocator.php
Normal file
460
system/Autoloader/FileLocator.php
Normal file
@@ -0,0 +1,460 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Autoloader;
|
||||
|
||||
/**
|
||||
* Class FileLocator
|
||||
*
|
||||
* Allows loading non-class files in a namespaced manner.
|
||||
* Works with Helpers, Views, etc.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
*/
|
||||
class FileLocator
|
||||
{
|
||||
/**
|
||||
* The Autoloader to use.
|
||||
*
|
||||
* @var \CodeIgniter\Autoloader\Autoloader
|
||||
*/
|
||||
protected $autoloader;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Autoloader $autoloader
|
||||
*/
|
||||
public function __construct(Autoloader $autoloader)
|
||||
{
|
||||
$this->autoloader = $autoloader;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempts to locate a file by examining the name for a namespace
|
||||
* and looking through the PSR-4 namespaced files that we know about.
|
||||
*
|
||||
* @param string $file The namespaced file to locate
|
||||
* @param string $folder The folder within the namespace that we should look for the file.
|
||||
* @param string $ext The file extension the file should have.
|
||||
*
|
||||
* @return string|false The path to the file, or false if not found.
|
||||
*/
|
||||
public function locateFile(string $file, string $folder = null, string $ext = 'php')
|
||||
{
|
||||
$file = $this->ensureExt($file, $ext);
|
||||
|
||||
// Clears the folder name if it is at the beginning of the filename
|
||||
if (! empty($folder) && ($pos = strpos($file, $folder)) === 0)
|
||||
{
|
||||
$file = substr($file, strlen($folder . '/'));
|
||||
}
|
||||
|
||||
// Is not namespaced? Try the application folder.
|
||||
if (strpos($file, '\\') === false)
|
||||
{
|
||||
return $this->legacyLocate($file, $folder);
|
||||
}
|
||||
|
||||
// Standardize slashes to handle nested directories.
|
||||
$file = strtr($file, '/', '\\');
|
||||
|
||||
$segments = explode('\\', $file);
|
||||
|
||||
// The first segment will be empty if a slash started the filename.
|
||||
if (empty($segments[0]))
|
||||
{
|
||||
unset($segments[0]);
|
||||
}
|
||||
|
||||
$paths = [];
|
||||
$prefix = '';
|
||||
$filename = '';
|
||||
|
||||
// Namespaces always comes with arrays of paths
|
||||
$namespaces = $this->autoloader->getNamespace();
|
||||
|
||||
while (! empty($segments))
|
||||
{
|
||||
$prefix .= empty($prefix) ? array_shift($segments) : '\\' . array_shift($segments);
|
||||
|
||||
if (empty($namespaces[$prefix]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$paths = $namespaces[$prefix];
|
||||
|
||||
$filename = implode('/', $segments);
|
||||
break;
|
||||
}
|
||||
|
||||
// if no namespaces matched then quit
|
||||
if (empty($paths))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check each path in the namespace
|
||||
foreach ($paths as $path)
|
||||
{
|
||||
// Ensure trailing slash
|
||||
$path = rtrim($path, '/') . '/';
|
||||
|
||||
// If we have a folder name, then the calling function
|
||||
// expects this file to be within that folder, like 'Views',
|
||||
// or 'libraries'.
|
||||
if (! empty($folder) && strpos($path . $filename, '/' . $folder . '/') === false)
|
||||
{
|
||||
$path .= trim($folder, '/') . '/';
|
||||
}
|
||||
|
||||
$path .= $filename;
|
||||
if (is_file($path))
|
||||
{
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Examines a file and returns the fully qualified domain name.
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClassname(string $file) : string
|
||||
{
|
||||
$php = file_get_contents($file);
|
||||
$tokens = token_get_all($php);
|
||||
$count = count($tokens);
|
||||
$dlm = false;
|
||||
$namespace = '';
|
||||
$class_name = '';
|
||||
|
||||
for ($i = 2; $i < $count; $i++)
|
||||
{
|
||||
if ((isset($tokens[$i - 2][1]) && ($tokens[$i - 2][1] === 'phpnamespace' || $tokens[$i - 2][1] === 'namespace')) || ($dlm && $tokens[$i - 1][0] === T_NS_SEPARATOR && $tokens[$i][0] === T_STRING))
|
||||
{
|
||||
if (! $dlm)
|
||||
{
|
||||
$namespace = 0;
|
||||
}
|
||||
if (isset($tokens[$i][1]))
|
||||
{
|
||||
$namespace = $namespace ? $namespace . '\\' . $tokens[$i][1] : $tokens[$i][1];
|
||||
$dlm = true;
|
||||
}
|
||||
}
|
||||
elseif ($dlm && ($tokens[$i][0] !== T_NS_SEPARATOR) && ($tokens[$i][0] !== T_STRING))
|
||||
{
|
||||
$dlm = false;
|
||||
}
|
||||
if (($tokens[$i - 2][0] === T_CLASS || (isset($tokens[$i - 2][1]) && $tokens[$i - 2][1] === 'phpclass'))
|
||||
&& $tokens[$i - 1][0] === T_WHITESPACE
|
||||
&& $tokens[$i][0] === T_STRING)
|
||||
{
|
||||
$class_name = $tokens[$i][1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty( $class_name ))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
return $namespace . '\\' . $class_name;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Searches through all of the defined namespaces looking for a file.
|
||||
* Returns an array of all found locations for the defined file.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $locator->search('Config/Routes.php');
|
||||
* // Assuming PSR4 namespaces include foo and bar, might return:
|
||||
* [
|
||||
* 'app/Modules/foo/Config/Routes.php',
|
||||
* 'app/Modules/bar/Config/Routes.php',
|
||||
* ]
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $ext
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function search(string $path, string $ext = 'php'): array
|
||||
{
|
||||
$path = $this->ensureExt($path, $ext);
|
||||
|
||||
$foundPaths = [];
|
||||
|
||||
foreach ($this->getNamespaces() as $namespace)
|
||||
{
|
||||
if (is_file($namespace['path'] . $path))
|
||||
{
|
||||
$foundPaths[] = $namespace['path'] . $path;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any duplicates
|
||||
$foundPaths = array_unique($foundPaths);
|
||||
|
||||
return $foundPaths;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Ensures a extension is at the end of a filename
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $ext
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function ensureExt(string $path, string $ext): string
|
||||
{
|
||||
if ($ext)
|
||||
{
|
||||
$ext = '.' . $ext;
|
||||
|
||||
if (substr($path, -strlen($ext)) !== $ext)
|
||||
{
|
||||
$path .= $ext;
|
||||
}
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return the namespace mappings we know about.
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
protected function getNamespaces()
|
||||
{
|
||||
$namespaces = [];
|
||||
|
||||
foreach ($this->autoloader->getNamespace() as $prefix => $paths)
|
||||
{
|
||||
foreach ($paths as $path)
|
||||
{
|
||||
$namespaces[] = [
|
||||
'prefix' => $prefix,
|
||||
'path' => rtrim($path, '\\/') . DIRECTORY_SEPARATOR,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $namespaces;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Find the qualified name of a file according to
|
||||
* the namespace of the first matched namespace path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string|false The qualified name or false if the path is not found
|
||||
*/
|
||||
public function findQualifiedNameFromPath(string $path)
|
||||
{
|
||||
$path = realpath($path);
|
||||
|
||||
if (! $path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->getNamespaces() as $namespace)
|
||||
{
|
||||
$namespace['path'] = realpath($namespace['path']);
|
||||
|
||||
if (empty($namespace['path']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mb_strpos($path, $namespace['path']) === 0)
|
||||
{
|
||||
$className = '\\' . $namespace['prefix'] . '\\' .
|
||||
ltrim(str_replace('/', '\\', mb_substr(
|
||||
$path, mb_strlen($namespace['path']))
|
||||
), '\\');
|
||||
// Remove the file extension (.php)
|
||||
$className = mb_substr($className, 0, -4);
|
||||
|
||||
return $className;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Scans the defined namespaces, returning a list of all files
|
||||
* that are contained within the subpath specified by $path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listFiles(string $path): array
|
||||
{
|
||||
if (empty($path))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$files = [];
|
||||
helper('filesystem');
|
||||
|
||||
foreach ($this->getNamespaces() as $namespace)
|
||||
{
|
||||
$fullPath = realpath($namespace['path'] . $path);
|
||||
|
||||
if (! is_dir($fullPath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$tempFiles = get_filenames($fullPath, true);
|
||||
|
||||
if (! empty($tempFiles))
|
||||
{
|
||||
$files = array_merge($files, $tempFiles);
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Scans the provided namespace, returning a list of all files
|
||||
* that are contained within the subpath specified by $path.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param string $path
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listNamespaceFiles(string $prefix, string $path): array
|
||||
{
|
||||
if (empty($path) || empty($prefix))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$files = [];
|
||||
helper('filesystem');
|
||||
|
||||
// autoloader->getNamespace($prefix) returns an array of paths for that namespace
|
||||
foreach ($this->autoloader->getNamespace($prefix) as $namespacePath)
|
||||
{
|
||||
$fullPath = realpath(rtrim($namespacePath, '/') . '/' . $path);
|
||||
|
||||
if (! is_dir($fullPath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$tempFiles = get_filenames($fullPath, true);
|
||||
|
||||
if (! empty($tempFiles))
|
||||
{
|
||||
$files = array_merge($files, $tempFiles);
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks the application folder to see if the file can be found.
|
||||
* Only for use with filenames that DO NOT include namespacing.
|
||||
*
|
||||
* @param string $file
|
||||
* @param string|null $folder
|
||||
*
|
||||
* @return string|false The path to the file, or false if not found.
|
||||
*/
|
||||
protected function legacyLocate(string $file, string $folder = null)
|
||||
{
|
||||
$paths = [
|
||||
APPPATH,
|
||||
SYSTEMPATH,
|
||||
];
|
||||
|
||||
foreach ($paths as $path)
|
||||
{
|
||||
$path .= empty($folder) ? $file : $folder . '/' . $file;
|
||||
|
||||
if (is_file($path))
|
||||
{
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
268
system/CLI/BaseCommand.php
Normal file
268
system/CLI/BaseCommand.php
Normal file
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\CLI;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Class BaseCommand
|
||||
*
|
||||
* @property $group
|
||||
* @property $name
|
||||
* @property $description
|
||||
*
|
||||
* @package CodeIgniter\CLI
|
||||
*/
|
||||
abstract class BaseCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group;
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* the Command's usage description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage;
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* the Command's options description
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* the Command's Arguments description
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* The Logger to use for a command
|
||||
*
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Instance of the CommandRunner controller
|
||||
* so commands can call other commands.
|
||||
*
|
||||
* @var \CodeIgniter\CLI\CommandRunner
|
||||
*/
|
||||
protected $commands;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* BaseCommand constructor.
|
||||
*
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* @param \CodeIgniter\CLI\CommandRunner $commands
|
||||
*/
|
||||
public function __construct(LoggerInterface $logger, CommandRunner $commands)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->commands = $commands;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
* This has to be over-ridden in any concrete implementation.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
abstract public function run(array $params);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Can be used by a command to run other commands.
|
||||
*
|
||||
* @param string $command
|
||||
* @param array $params
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function call(string $command, array $params = [])
|
||||
{
|
||||
// The CommandRunner will grab the first element
|
||||
// for the command name.
|
||||
array_unshift($params, $command);
|
||||
|
||||
return $this->commands->index($params);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A simple method to display an error with line/file,
|
||||
* in child commands.
|
||||
*
|
||||
* @param \Exception $e
|
||||
*/
|
||||
protected function showError(\Exception $e)
|
||||
{
|
||||
CLI::newLine();
|
||||
CLI::error($e->getMessage());
|
||||
CLI::write($e->getFile() . ' - ' . $e->getLine());
|
||||
CLI::newLine();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Makes it simple to access our protected properties.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $key)
|
||||
{
|
||||
if (isset($this->$key))
|
||||
{
|
||||
return $this->$key;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Makes it simple to check our protected properties.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function __isset(string $key): bool
|
||||
{
|
||||
return isset($this->$key);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* show Help include (usage,arguments,description,options)
|
||||
*/
|
||||
public function showHelp()
|
||||
{
|
||||
// 4 spaces instead of tab
|
||||
$tab = ' ';
|
||||
CLI::write(lang('CLI.helpDescription'), 'yellow');
|
||||
CLI::write($tab . $this->description);
|
||||
CLI::newLine();
|
||||
|
||||
CLI::write(lang('CLI.helpUsage'), 'yellow');
|
||||
$usage = empty($this->usage) ? $this->name . ' [arguments]' : $this->usage;
|
||||
CLI::write($tab . $usage);
|
||||
CLI::newLine();
|
||||
|
||||
$pad = max($this->getPad($this->options, 6), $this->getPad($this->arguments, 6));
|
||||
|
||||
if (! empty($this->arguments))
|
||||
{
|
||||
CLI::write(lang('CLI.helpArguments'), 'yellow');
|
||||
foreach ($this->arguments as $argument => $description)
|
||||
{
|
||||
CLI::write($tab . CLI::color(str_pad($argument, $pad), 'green') . $description, 'yellow');
|
||||
}
|
||||
CLI::newLine();
|
||||
}
|
||||
|
||||
if (! empty($this->options))
|
||||
{
|
||||
CLI::write(lang('CLI.helpOptions'), 'yellow');
|
||||
foreach ($this->options as $option => $description)
|
||||
{
|
||||
CLI::write($tab . CLI::color(str_pad($option, $pad), 'green') . $description, 'yellow');
|
||||
}
|
||||
CLI::newLine();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get pad for $key => $value array output
|
||||
*
|
||||
* @param array $array
|
||||
* @param integer $pad
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getPad(array $array, int $pad): int
|
||||
{
|
||||
$max = 0;
|
||||
foreach ($array as $key => $value)
|
||||
{
|
||||
$max = max($max, strlen($key));
|
||||
}
|
||||
return $max + $pad;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
970
system/CLI/CLI.php
Normal file
970
system/CLI/CLI.php
Normal file
@@ -0,0 +1,970 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\CLI;
|
||||
|
||||
use CodeIgniter\CLI\Exceptions\CLIException;
|
||||
|
||||
/**
|
||||
* Set of static methods useful for CLI request handling.
|
||||
*
|
||||
* Portions of this code were initially from the FuelPHP Framework,
|
||||
* version 1.7.x, and used here under the MIT license they were
|
||||
* originally made available under. Reference: http://fuelphp.com
|
||||
*
|
||||
* Some of the code in this class is Windows-specific, and not
|
||||
* possible to test using travis-ci. It has been phpunit-annotated
|
||||
* to prevent messing up code coverage.
|
||||
*
|
||||
* Some of the methods require keyboard input, and are not unit-testable
|
||||
* as a result: input() and prompt().
|
||||
* validate() is internal, and not testable if prompt() isn't.
|
||||
* The wait() method is mostly testable, as long as you don't give it
|
||||
* an argument of "0".
|
||||
* These have been flagged to ignore for code coverage purposes.
|
||||
*
|
||||
* @package CodeIgniter\CLI
|
||||
*/
|
||||
class CLI
|
||||
{
|
||||
|
||||
/**
|
||||
* Is the readline library on the system?
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public static $readline_support = false;
|
||||
|
||||
/**
|
||||
* The message displayed at prompts.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $wait_msg = 'Press any key to continue...';
|
||||
|
||||
/**
|
||||
* Has the class already been initialized?
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected static $initialized = false;
|
||||
|
||||
/**
|
||||
* Foreground color list
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $foreground_colors = [
|
||||
'black' => '0;30',
|
||||
'dark_gray' => '1;30',
|
||||
'blue' => '0;34',
|
||||
'dark_blue' => '1;34',
|
||||
'light_blue' => '1;34',
|
||||
'green' => '0;32',
|
||||
'light_green' => '1;32',
|
||||
'cyan' => '0;36',
|
||||
'light_cyan' => '1;36',
|
||||
'red' => '0;31',
|
||||
'light_red' => '1;31',
|
||||
'purple' => '0;35',
|
||||
'light_purple' => '1;35',
|
||||
'light_yellow' => '0;33',
|
||||
'yellow' => '1;33',
|
||||
'light_gray' => '0;37',
|
||||
'white' => '1;37',
|
||||
];
|
||||
|
||||
/**
|
||||
* Background color list
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $background_colors = [
|
||||
'black' => '40',
|
||||
'red' => '41',
|
||||
'green' => '42',
|
||||
'yellow' => '43',
|
||||
'blue' => '44',
|
||||
'magenta' => '45',
|
||||
'cyan' => '46',
|
||||
'light_gray' => '47',
|
||||
];
|
||||
|
||||
/**
|
||||
* List of array segments.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $segments = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $options = [];
|
||||
|
||||
/**
|
||||
* Helps track internally whether the last
|
||||
* output was a "write" or a "print" to
|
||||
* keep the output clean and as expected.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $lastWrite;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Static "constructor".
|
||||
*/
|
||||
public static function init()
|
||||
{
|
||||
// Readline is an extension for PHP that makes interactivity with PHP
|
||||
// much more bash-like.
|
||||
// http://www.php.net/manual/en/readline.installation.php
|
||||
static::$readline_support = extension_loaded('readline');
|
||||
|
||||
// clear segments & options to keep testing clean
|
||||
static::$segments = [];
|
||||
static::$options = [];
|
||||
|
||||
static::parseCommandLine();
|
||||
|
||||
static::$initialized = true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get input from the shell, using readline or the standard STDIN
|
||||
*
|
||||
* Named options must be in the following formats:
|
||||
* php index.php user -v --v -name=John --name=John
|
||||
*
|
||||
* @param string $prefix
|
||||
* @return string
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function input(string $prefix = null): string
|
||||
{
|
||||
if (static::$readline_support)
|
||||
{
|
||||
return readline($prefix);
|
||||
}
|
||||
|
||||
echo $prefix;
|
||||
|
||||
return fgets(STDIN);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Asks the user for input.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* // Takes any input
|
||||
* $color = CLI::prompt('What is your favorite color?');
|
||||
*
|
||||
* // Takes any input, but offers default
|
||||
* $color = CLI::prompt('What is your favourite color?', 'white');
|
||||
*
|
||||
* // Will validate options with the in_list rule and accept only if one of the list
|
||||
* $color = CLI::prompt('What is your favourite color?', array('red','blue'));
|
||||
*
|
||||
* // Do not provide options but requires a valid email
|
||||
* $email = CLI::prompt('What is your email?', null, 'required|valid_email');
|
||||
*
|
||||
* @param string $field Output "field" question
|
||||
* @param string|array $options String to a default value, array to a list of options (the first option will be the default value)
|
||||
* @param string $validation Validation rules
|
||||
*
|
||||
* @return string The user input
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function prompt(string $field, $options = null, string $validation = null): string
|
||||
{
|
||||
$extra_output = '';
|
||||
$default = '';
|
||||
|
||||
if (is_string($options))
|
||||
{
|
||||
$extra_output = ' [' . static::color($options, 'white') . ']';
|
||||
$default = $options;
|
||||
}
|
||||
|
||||
if (is_array($options) && $options)
|
||||
{
|
||||
$opts = $options;
|
||||
$extra_output_default = static::color($opts[0], 'white');
|
||||
|
||||
unset($opts[0]);
|
||||
|
||||
if (empty($opts))
|
||||
{
|
||||
$extra_output = $extra_output_default;
|
||||
}
|
||||
else
|
||||
{
|
||||
$extra_output = ' [' . $extra_output_default . ', ' . implode(', ', $opts) . ']';
|
||||
$validation .= '|in_list[' . implode(',', $options) . ']';
|
||||
$validation = trim($validation, '|');
|
||||
}
|
||||
|
||||
$default = $options[0];
|
||||
}
|
||||
|
||||
fwrite(STDOUT, $field . $extra_output . ': ');
|
||||
|
||||
// Read the input from keyboard.
|
||||
$input = trim(static::input()) ?: $default;
|
||||
|
||||
if (isset($validation))
|
||||
{
|
||||
while (! static::validate($field, $input, $validation))
|
||||
{
|
||||
$input = static::prompt($field, $options, $validation);
|
||||
}
|
||||
}
|
||||
|
||||
return empty($input) ? '' : $input;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Validate one prompt "field" at a time
|
||||
*
|
||||
* @param string $field Prompt "field" output
|
||||
* @param string $value Input value
|
||||
* @param string $rules Validation rules
|
||||
*
|
||||
* @return boolean
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
protected static function validate(string $field, string $value, string $rules): bool
|
||||
{
|
||||
$validation = \Config\Services::validation(null, false);
|
||||
$validation->setRule($field, null, $rules);
|
||||
$validation->run([$field => $value]);
|
||||
|
||||
if ($validation->hasError($field))
|
||||
{
|
||||
static::error($validation->getError($field));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Outputs a string to the CLI without any surrounding newlines.
|
||||
* Useful for showing repeating elements on a single line.
|
||||
*
|
||||
* @param string $text
|
||||
* @param string|null $foreground
|
||||
* @param string|null $background
|
||||
*/
|
||||
public static function print(string $text = '', string $foreground = null, string $background = null)
|
||||
{
|
||||
if ($foreground || $background)
|
||||
{
|
||||
$text = static::color($text, $foreground, $background);
|
||||
}
|
||||
|
||||
static::$lastWrite = null;
|
||||
|
||||
fwrite(STDOUT, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a string to the cli on it's own line.
|
||||
*
|
||||
* @param string $text The text to output
|
||||
* @param string $foreground
|
||||
* @param string $background
|
||||
*/
|
||||
public static function write(string $text = '', string $foreground = null, string $background = null)
|
||||
{
|
||||
if ($foreground || $background)
|
||||
{
|
||||
$text = static::color($text, $foreground, $background);
|
||||
}
|
||||
|
||||
if (static::$lastWrite !== 'write')
|
||||
{
|
||||
$text = PHP_EOL . $text;
|
||||
static::$lastWrite = 'write';
|
||||
}
|
||||
|
||||
fwrite(STDOUT, $text . PHP_EOL);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Outputs an error to the CLI using STDERR instead of STDOUT
|
||||
*
|
||||
* @param string|array $text The text to output, or array of errors
|
||||
* @param string $foreground
|
||||
* @param string $background
|
||||
*/
|
||||
public static function error(string $text, string $foreground = 'light_red', string $background = null)
|
||||
{
|
||||
if ($foreground || $background)
|
||||
{
|
||||
$text = static::color($text, $foreground, $background);
|
||||
}
|
||||
|
||||
fwrite(STDERR, $text . PHP_EOL);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Beeps a certain number of times.
|
||||
*
|
||||
* @param integer $num The number of times to beep
|
||||
*/
|
||||
public static function beep(int $num = 1)
|
||||
{
|
||||
echo str_repeat("\x07", $num);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Waits a certain number of seconds, optionally showing a wait message and
|
||||
* waiting for a key press.
|
||||
*
|
||||
* @param integer $seconds Number of seconds
|
||||
* @param boolean $countdown Show a countdown or not
|
||||
*/
|
||||
public static function wait(int $seconds, bool $countdown = false)
|
||||
{
|
||||
if ($countdown === true)
|
||||
{
|
||||
$time = $seconds;
|
||||
|
||||
while ($time > 0)
|
||||
{
|
||||
fwrite(STDOUT, $time . '... ');
|
||||
sleep(1);
|
||||
$time --;
|
||||
}
|
||||
static::write();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($seconds > 0)
|
||||
{
|
||||
sleep($seconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this chunk cannot be tested because of keyboard input
|
||||
// @codeCoverageIgnoreStart
|
||||
static::write(static::$wait_msg);
|
||||
static::input();
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* if operating system === windows
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isWindows(): bool
|
||||
{
|
||||
return stripos(PHP_OS, 'WIN') === 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Enter a number of empty lines
|
||||
*
|
||||
* @param integer $num Number of lines to output
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function newLine(int $num = 1)
|
||||
{
|
||||
// Do it once or more, write with empty string gives us a new line
|
||||
for ($i = 0; $i < $num; $i ++)
|
||||
{
|
||||
static::write('');
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Clears the screen of output
|
||||
*
|
||||
* @return void
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function clearScreen()
|
||||
{
|
||||
static::isWindows()
|
||||
|
||||
// Windows is a bit crap at this, but their terminal is tiny so shove this in
|
||||
? static::newLine(40)
|
||||
|
||||
// Anything with a flair of Unix will handle these magic characters
|
||||
: fwrite(STDOUT, chr(27) . '[H' . chr(27) . '[2J');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the given text with the correct color codes for a foreground and
|
||||
* optionally a background color.
|
||||
*
|
||||
* @param string $text The text to color
|
||||
* @param string $foreground The foreground color
|
||||
* @param string $background The background color
|
||||
* @param string $format Other formatting to apply. Currently only 'underline' is understood
|
||||
*
|
||||
* @return string The color coded string
|
||||
*/
|
||||
public static function color(string $text, string $foreground, string $background = null, string $format = null): string
|
||||
{
|
||||
if (static::isWindows() && ! isset($_SERVER['ANSICON']))
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
return $text;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if (! array_key_exists($foreground, static::$foreground_colors))
|
||||
{
|
||||
throw CLIException::forInvalidColor('foreground', $foreground);
|
||||
}
|
||||
|
||||
if ($background !== null && ! array_key_exists($background, static::$background_colors))
|
||||
{
|
||||
throw CLIException::forInvalidColor('background', $background);
|
||||
}
|
||||
|
||||
$string = "\033[" . static::$foreground_colors[$foreground] . 'm';
|
||||
|
||||
if ($background !== null)
|
||||
{
|
||||
$string .= "\033[" . static::$background_colors[$background] . 'm';
|
||||
}
|
||||
|
||||
if ($format === 'underline')
|
||||
{
|
||||
$string .= "\033[4m";
|
||||
}
|
||||
|
||||
$string .= $text . "\033[0m";
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get the number of characters in string having encoded characters
|
||||
* and ignores styles set by the color() function
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function strlen(?string $string): int
|
||||
{
|
||||
if (is_null($string))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
foreach (static::$foreground_colors as $color)
|
||||
{
|
||||
$string = strtr($string, ["\033[" . $color . 'm' => '']);
|
||||
}
|
||||
|
||||
foreach (static::$background_colors as $color)
|
||||
{
|
||||
$string = strtr($string, ["\033[" . $color . 'm' => '']);
|
||||
}
|
||||
|
||||
$string = strtr($string, ["\033[4m" => '', "\033[0m" => '']);
|
||||
|
||||
return mb_strlen($string);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempts to determine the width of the viewable CLI window.
|
||||
* This only works on *nix-based systems, so return a sane default
|
||||
* for Windows environments.
|
||||
*
|
||||
* @param integer $default
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function getWidth(int $default = 80): int
|
||||
{
|
||||
if (static::isWindows() || (int) shell_exec('tput cols') === 0)
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
return $default;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
return (int) shell_exec('tput cols');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempts to determine the height of the viewable CLI window.
|
||||
* This only works on *nix-based systems, so return a sane default
|
||||
* for Windows environments.
|
||||
*
|
||||
* @param integer $default
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function getHeight(int $default = 32): int
|
||||
{
|
||||
if (static::isWindows())
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
return $default;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
return (int) shell_exec('tput lines');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Displays a progress bar on the CLI. You must call it repeatedly
|
||||
* to update it. Set $thisStep = false to erase the progress bar.
|
||||
*
|
||||
* @param integer|boolean $thisStep
|
||||
* @param integer $totalSteps
|
||||
*/
|
||||
public static function showProgress($thisStep = 1, int $totalSteps = 10)
|
||||
{
|
||||
static $inProgress = false;
|
||||
|
||||
// restore cursor position when progress is continuing.
|
||||
if ($inProgress !== false && $inProgress <= $thisStep)
|
||||
{
|
||||
fwrite(STDOUT, "\033[1A");
|
||||
}
|
||||
$inProgress = $thisStep;
|
||||
|
||||
if ($thisStep !== false)
|
||||
{
|
||||
// Don't allow div by zero or negative numbers....
|
||||
$thisStep = abs($thisStep);
|
||||
$totalSteps = $totalSteps < 1 ? 1 : $totalSteps;
|
||||
|
||||
$percent = intval(($thisStep / $totalSteps) * 100);
|
||||
$step = (int) round($percent / 10);
|
||||
|
||||
// Write the progress bar
|
||||
fwrite(STDOUT, "[\033[32m" . str_repeat('#', $step) . str_repeat('.', 10 - $step) . "\033[0m]");
|
||||
// Textual representation...
|
||||
fwrite(STDOUT, sprintf(' %3d%% Complete', $percent) . PHP_EOL);
|
||||
}
|
||||
else
|
||||
{
|
||||
fwrite(STDOUT, "\007");
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes a string and writes it to the command line, wrapping to a maximum
|
||||
* width. If no maximum width is specified, will wrap to the window's max
|
||||
* width.
|
||||
*
|
||||
* If an int is passed into $pad_left, then all strings after the first
|
||||
* will padded with that many spaces to the left. Useful when printing
|
||||
* short descriptions that need to start on an existing line.
|
||||
*
|
||||
* @param string $string
|
||||
* @param integer $max
|
||||
* @param integer $pad_left
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function wrap(string $string = null, int $max = 0, int $pad_left = 0): string
|
||||
{
|
||||
if (empty($string))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($max === 0)
|
||||
{
|
||||
$max = CLI::getWidth();
|
||||
}
|
||||
|
||||
if (CLI::getWidth() < $max)
|
||||
{
|
||||
$max = CLI::getWidth();
|
||||
}
|
||||
|
||||
$max = $max - $pad_left;
|
||||
|
||||
$lines = wordwrap($string, $max);
|
||||
|
||||
if ($pad_left > 0)
|
||||
{
|
||||
$lines = explode(PHP_EOL, $lines);
|
||||
|
||||
$first = true;
|
||||
|
||||
array_walk($lines, function (&$line, $index) use ($pad_left, &$first) {
|
||||
if (! $first)
|
||||
{
|
||||
$line = str_repeat(' ', $pad_left) . $line;
|
||||
}
|
||||
else
|
||||
{
|
||||
$first = false;
|
||||
}
|
||||
});
|
||||
|
||||
$lines = implode(PHP_EOL, $lines);
|
||||
}
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------
|
||||
// Command-Line 'URI' support
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parses the command line it was called from and collects all
|
||||
* options and valid segments.
|
||||
*
|
||||
* I tried to use getopt but had it fail occasionally to find any
|
||||
* options but argc has always had our back. We don't have all of the power
|
||||
* of getopt but this does us just fine.
|
||||
*/
|
||||
protected static function parseCommandLine()
|
||||
{
|
||||
$optionsFound = false;
|
||||
|
||||
// start picking segments off from #1, ignoring the invoking program
|
||||
for ($i = 1; $i < $_SERVER['argc']; $i ++)
|
||||
{
|
||||
// If there's no '-' at the beginning of the argument
|
||||
// then add it to our segments.
|
||||
if (! $optionsFound && mb_strpos($_SERVER['argv'][$i], '-') === false)
|
||||
{
|
||||
static::$segments[] = $_SERVER['argv'][$i];
|
||||
continue;
|
||||
}
|
||||
|
||||
// We set $optionsFound here so that we know to
|
||||
// skip the next argument since it's likely the
|
||||
// value belonging to this option.
|
||||
$optionsFound = true;
|
||||
|
||||
$arg = str_replace('-', '', $_SERVER['argv'][$i]);
|
||||
$value = null;
|
||||
|
||||
// if there is a following segment, and it doesn't start with a dash, it's a value.
|
||||
if (isset($_SERVER['argv'][$i + 1]) && mb_strpos($_SERVER['argv'][$i + 1], '-') !== 0)
|
||||
{
|
||||
$value = $_SERVER['argv'][$i + 1];
|
||||
$i ++;
|
||||
}
|
||||
|
||||
static::$options[$arg] = $value;
|
||||
|
||||
// Reset $optionsFound so it can collect segments
|
||||
// past any options.
|
||||
$optionsFound = false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the command line string portions of the arguments, minus
|
||||
* any options, as a string. This is used to pass along to the main
|
||||
* CodeIgniter application.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getURI(): string
|
||||
{
|
||||
return implode('/', static::$segments);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an individual segment.
|
||||
*
|
||||
* This ignores any options that might have been dispersed between
|
||||
* valid segments in the command:
|
||||
*
|
||||
* // segment(3) is 'three', not '-f' or 'anOption'
|
||||
* > php spark one two -f anOption three
|
||||
*
|
||||
* @param integer $index
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function getSegment(int $index)
|
||||
{
|
||||
if (! isset(static::$segments[$index - 1]))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return static::$segments[$index - 1];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the raw array of segments found.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getSegments(): array
|
||||
{
|
||||
return static::$segments;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets a single command-line option. Returns TRUE if the option
|
||||
* exists, but doesn't have a value, and is simply acting as a flag.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return boolean|mixed|null
|
||||
*/
|
||||
public static function getOption(string $name)
|
||||
{
|
||||
if (! array_key_exists($name, static::$options))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the option didn't have a value, simply return TRUE
|
||||
// so they know it was set, otherwise return the actual value.
|
||||
$val = static::$options[$name] === null ? true : static::$options[$name];
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the raw array of options found.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getOptions(): array
|
||||
{
|
||||
return static::$options;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the options as a string, suitable for passing along on
|
||||
* the CLI to other commands.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getOptionString(): string
|
||||
{
|
||||
if (empty(static::$options))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
$out = '';
|
||||
|
||||
foreach (static::$options as $name => $value)
|
||||
{
|
||||
// If there's a space, we need to group
|
||||
// so it will pass correctly.
|
||||
if (mb_strpos($value, ' ') !== false)
|
||||
{
|
||||
$value = '"' . $value . '"';
|
||||
}
|
||||
|
||||
$out .= "-{$name} $value ";
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a well formatted table
|
||||
*
|
||||
* @param array $tbody List of rows
|
||||
* @param array $thead List of columns
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function table(array $tbody, array $thead = [])
|
||||
{
|
||||
// All the rows in the table will be here until the end
|
||||
$table_rows = [];
|
||||
|
||||
// We need only indexes and not keys
|
||||
if (! empty($thead))
|
||||
{
|
||||
$table_rows[] = array_values($thead);
|
||||
}
|
||||
|
||||
foreach ($tbody as $tr)
|
||||
{
|
||||
$table_rows[] = array_values($tr);
|
||||
}
|
||||
|
||||
// Yes, it really is necessary to know this count
|
||||
$total_rows = count($table_rows);
|
||||
|
||||
// Store all columns lengths
|
||||
// $all_cols_lengths[row][column] = length
|
||||
$all_cols_lengths = [];
|
||||
|
||||
// Store maximum lengths by column
|
||||
// $max_cols_lengths[column] = length
|
||||
$max_cols_lengths = [];
|
||||
|
||||
// Read row by row and define the longest columns
|
||||
for ($row = 0; $row < $total_rows; $row ++)
|
||||
{
|
||||
$column = 0; // Current column index
|
||||
foreach ($table_rows[$row] as $col)
|
||||
{
|
||||
// Sets the size of this column in the current row
|
||||
$all_cols_lengths[$row][$column] = static::strlen($col);
|
||||
|
||||
// If the current column does not have a value among the larger ones
|
||||
// or the value of this is greater than the existing one
|
||||
// then, now, this assumes the maximum length
|
||||
if (! isset($max_cols_lengths[$column]) || $all_cols_lengths[$row][$column] > $max_cols_lengths[$column])
|
||||
{
|
||||
$max_cols_lengths[$column] = $all_cols_lengths[$row][$column];
|
||||
}
|
||||
|
||||
// We can go check the size of the next column...
|
||||
$column ++;
|
||||
}
|
||||
}
|
||||
|
||||
// Read row by row and add spaces at the end of the columns
|
||||
// to match the exact column length
|
||||
for ($row = 0; $row < $total_rows; $row ++)
|
||||
{
|
||||
$column = 0;
|
||||
foreach ($table_rows[$row] as $col)
|
||||
{
|
||||
$diff = $max_cols_lengths[$column] - static::strlen($col);
|
||||
if ($diff)
|
||||
{
|
||||
$table_rows[$row][$column] = $table_rows[$row][$column] . str_repeat(' ', $diff);
|
||||
}
|
||||
$column ++;
|
||||
}
|
||||
}
|
||||
|
||||
$table = '';
|
||||
|
||||
// Joins columns and append the well formatted rows to the table
|
||||
for ($row = 0; $row < $total_rows; $row ++)
|
||||
{
|
||||
// Set the table border-top
|
||||
if ($row === 0)
|
||||
{
|
||||
$cols = '+';
|
||||
foreach ($table_rows[$row] as $col)
|
||||
{
|
||||
$cols .= str_repeat('-', static::strlen($col) + 2) . '+';
|
||||
}
|
||||
$table .= $cols . PHP_EOL;
|
||||
}
|
||||
|
||||
// Set the columns borders
|
||||
$table .= '| ' . implode(' | ', $table_rows[$row]) . ' |' . PHP_EOL;
|
||||
|
||||
// Set the thead and table borders-bottom
|
||||
if ($row === 0 && ! empty($thead) || $row + 1 === $total_rows)
|
||||
{
|
||||
$table .= $cols . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
fwrite(STDOUT, $table);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
|
||||
// Ensure the class is initialized. Done outside of code coverage
|
||||
// @codeCoverageIgnoreStart
|
||||
CLI::init();
|
||||
// @codeCoverageIgnoreEnd
|
||||
212
system/CLI/CommandRunner.php
Normal file
212
system/CLI/CommandRunner.php
Normal file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\CLI;
|
||||
|
||||
use CodeIgniter\Config\Services;
|
||||
use CodeIgniter\Controller;
|
||||
|
||||
/**
|
||||
* Command runner
|
||||
*/
|
||||
class CommandRunner extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Stores the info about found Commands.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [];
|
||||
|
||||
/**
|
||||
* Message logger.
|
||||
*
|
||||
* @var \CodeIgniter\Log\Logger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* We map all un-routed CLI methods through this function
|
||||
* so we have the chance to look for a Command first.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array ...$params
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function _remap($method, ...$params)
|
||||
{
|
||||
// The first param is usually empty, so scrap it.
|
||||
if (empty($params[0]))
|
||||
{
|
||||
array_shift($params);
|
||||
}
|
||||
|
||||
return $this->index($params);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Default command.
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function index(array $params)
|
||||
{
|
||||
$command = array_shift($params);
|
||||
|
||||
$this->createCommandList();
|
||||
|
||||
if (is_null($command))
|
||||
{
|
||||
$command = 'help';
|
||||
}
|
||||
|
||||
return $this->runCommand($command, $params);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Actually runs the command.
|
||||
*
|
||||
* @param string $command
|
||||
* @param array $params
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function runCommand(string $command, array $params)
|
||||
{
|
||||
if (! isset($this->commands[$command]))
|
||||
{
|
||||
CLI::error(lang('CLI.commandNotFound', [$command]));
|
||||
CLI::newLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// The file would have already been loaded during the
|
||||
// createCommandList function...
|
||||
$className = $this->commands[$command]['class'];
|
||||
$class = new $className($this->logger, $this);
|
||||
|
||||
return $class->run($params);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Scans all Commands directories and prepares a list
|
||||
* of each command with it's group and file.
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function createCommandList()
|
||||
{
|
||||
$files = Services::locator()->listFiles('Commands/');
|
||||
|
||||
// If no matching command files were found, bail
|
||||
if (empty($files))
|
||||
{
|
||||
// This should never happen in unit testing.
|
||||
// if it does, we have far bigger problems!
|
||||
// @codeCoverageIgnoreStart
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
// Loop over each file checking to see if a command with that
|
||||
// alias exists in the class. If so, return it. Otherwise, try the next.
|
||||
foreach ($files as $file)
|
||||
{
|
||||
$className = Services::locator()->findQualifiedNameFromPath($file);
|
||||
if (empty($className) || ! class_exists($className))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$class = new \ReflectionClass($className);
|
||||
|
||||
if (! $class->isInstantiable() || ! $class->isSubclassOf(BaseCommand::class))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$class = new $className($this->logger, $this);
|
||||
|
||||
// Store it!
|
||||
if ($class->group !== null)
|
||||
{
|
||||
$this->commands[$class->name] = [
|
||||
'class' => $className,
|
||||
'file' => $file,
|
||||
'group' => $class->group,
|
||||
'description' => $class->description,
|
||||
];
|
||||
}
|
||||
|
||||
$class = null;
|
||||
unset($class);
|
||||
}
|
||||
|
||||
asort($this->commands);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Allows access to the current commands that have been found.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCommands(): array
|
||||
{
|
||||
return $this->commands;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
106
system/CLI/Console.php
Normal file
106
system/CLI/Console.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\CLI;
|
||||
|
||||
use CodeIgniter\CodeIgniter;
|
||||
|
||||
/**
|
||||
* Console
|
||||
*/
|
||||
class Console
|
||||
{
|
||||
|
||||
/**
|
||||
* Main CodeIgniter instance.
|
||||
*
|
||||
* @var CodeIgniter
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Console constructor.
|
||||
*
|
||||
* @param \CodeIgniter\CodeIgniter $app
|
||||
*/
|
||||
public function __construct(CodeIgniter $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Runs the current command discovered on the CLI.
|
||||
*
|
||||
* @param boolean $useSafeOutput
|
||||
*
|
||||
* @return \CodeIgniter\HTTP\RequestInterface|\CodeIgniter\HTTP\Response|\CodeIgniter\HTTP\ResponseInterface|mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function run(bool $useSafeOutput = false)
|
||||
{
|
||||
$path = CLI::getURI() ?: 'list';
|
||||
|
||||
// Set the path for the application to route to.
|
||||
$this->app->setPath("ci{$path}");
|
||||
|
||||
return $this->app->useSafeOutput($useSafeOutput)->run();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Displays basic information about the Console.
|
||||
*/
|
||||
public function showHeader()
|
||||
{
|
||||
CLI::newLine(1);
|
||||
|
||||
CLI::write(CLI::color('CodeIgniter CLI Tool', 'green')
|
||||
. ' - Version ' . CodeIgniter::CI_VERSION
|
||||
. ' - Server-Time: ' . date('Y-m-d H:i:sa'));
|
||||
|
||||
CLI::newLine(1);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
15
system/CLI/Exceptions/CLIException.php
Normal file
15
system/CLI/Exceptions/CLIException.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php namespace CodeIgniter\CLI\Exceptions;
|
||||
|
||||
class CLIException extends \RuntimeException
|
||||
{
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $color
|
||||
*
|
||||
* @return \CodeIgniter\CLI\Exceptions\CLIException
|
||||
*/
|
||||
public static function forInvalidColor(string $type, string $color)
|
||||
{
|
||||
return new static(lang('CLI.invalidColor', [$type, $color]));
|
||||
}
|
||||
}
|
||||
117
system/Cache/CacheFactory.php
Normal file
117
system/Cache/CacheFactory.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Cache;
|
||||
|
||||
use CodeIgniter\Cache\Exceptions\CacheException;
|
||||
use CodeIgniter\Exceptions\CriticalError;
|
||||
|
||||
/**
|
||||
* Class Cache
|
||||
*
|
||||
* A factory for loading the desired
|
||||
*
|
||||
* @package CodeIgniter\Cache
|
||||
*/
|
||||
class CacheFactory
|
||||
{
|
||||
/**
|
||||
* Attempts to create the desired cache handler, based upon the
|
||||
*
|
||||
* @param $config
|
||||
* @param string $handler
|
||||
* @param string $backup
|
||||
*
|
||||
* @return \CodeIgniter\Cache\CacheInterface
|
||||
*/
|
||||
public static function getHandler($config, string $handler = null, string $backup = null)
|
||||
{
|
||||
if (! isset($config->validHandlers) || ! is_array($config->validHandlers))
|
||||
{
|
||||
throw CacheException::forInvalidHandlers();
|
||||
}
|
||||
|
||||
if (! isset($config->handler) || ! isset($config->backupHandler))
|
||||
{
|
||||
throw CacheException::forNoBackup();
|
||||
}
|
||||
|
||||
$handler = ! empty($handler) ? $handler : $config->handler;
|
||||
$backup = ! empty($backup) ? $backup : $config->backupHandler;
|
||||
|
||||
if (! array_key_exists($handler, $config->validHandlers) || ! array_key_exists($backup, $config->validHandlers))
|
||||
{
|
||||
throw CacheException::forHandlerNotFound();
|
||||
}
|
||||
|
||||
// Get an instance of our handler.
|
||||
$adapter = new $config->validHandlers[$handler]($config);
|
||||
|
||||
if (! $adapter->isSupported())
|
||||
{
|
||||
$adapter = new $config->validHandlers[$backup]($config);
|
||||
|
||||
if (! $adapter->isSupported())
|
||||
{
|
||||
// Log stuff here, don't throw exception. No need to raise a fuss.
|
||||
// Fall back to the dummy adapter.
|
||||
$adapter = new $config->validHandlers['dummy']();
|
||||
}
|
||||
}
|
||||
|
||||
// If $adapter->initialization throws a CriticalError exception, we will attempt to
|
||||
// use the $backup handler, if that also fails, we resort to the dummy handler.
|
||||
try
|
||||
{
|
||||
$adapter->initialize();
|
||||
}
|
||||
catch (CriticalError $e)
|
||||
{
|
||||
// log the fact that an exception occurred as well what handler we are resorting to
|
||||
log_message('critical', $e->getMessage() . ' Resorting to using ' . $backup . ' handler.');
|
||||
|
||||
// get the next best cache handler (or dummy if the $backup also fails)
|
||||
$adapter = self::getHandler($config, $backup, 'dummy');
|
||||
}
|
||||
|
||||
return $adapter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
155
system/Cache/CacheInterface.php
Normal file
155
system/Cache/CacheInterface.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Cache;
|
||||
|
||||
/**
|
||||
* Cache interface
|
||||
*/
|
||||
|
||||
interface CacheInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Takes care of any handler-specific setup that must be done.
|
||||
*/
|
||||
public function initialize();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempts to fetch an item from the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Saves an item to the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
* @param mixed $value The data to save
|
||||
* @param integer $ttl Time To Live, in seconds (default 60)
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function save(string $key, $value, int $ttl = 60);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Deletes a specific item from the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(string $key);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic incrementation of a raw stored value.
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function increment(string $key, int $offset = 1);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic decrementation of a raw stored value.
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decrement(string $key, int $offset = 1);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will delete all items in the entire cache.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function clean();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns information on the entire cache.
|
||||
*
|
||||
* The information returned and the structure of the data
|
||||
* varies depending on the handler.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCacheInfo();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns detailed information about the specific item in the cache.
|
||||
*
|
||||
* @param string $key Cache item name.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetaData(string $key);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the driver is supported on this system.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSupported(): bool;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
36
system/Cache/Exceptions/CacheException.php
Normal file
36
system/Cache/Exceptions/CacheException.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php namespace CodeIgniter\Cache\Exceptions;
|
||||
|
||||
class CacheException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
/**
|
||||
* @return \CodeIgniter\Cache\Exceptions\CacheException
|
||||
*/
|
||||
public static function forUnableToWrite(string $path)
|
||||
{
|
||||
return new static(lang('Cache.unableToWrite', [$path]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \CodeIgniter\Cache\Exceptions\CacheException
|
||||
*/
|
||||
public static function forInvalidHandlers()
|
||||
{
|
||||
return new static(lang('Cache.invalidHandlers'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \CodeIgniter\Cache\Exceptions\CacheException
|
||||
*/
|
||||
public static function forNoBackup()
|
||||
{
|
||||
return new static(lang('Cache.noBackup'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \CodeIgniter\Cache\Exceptions\CacheException
|
||||
*/
|
||||
public static function forHandlerNotFound()
|
||||
{
|
||||
return new static(lang('Cache.handlerNotFound'));
|
||||
}
|
||||
}
|
||||
12
system/Cache/Exceptions/ExceptionInterface.php
Normal file
12
system/Cache/Exceptions/ExceptionInterface.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php namespace CodeIgniter\Cache\Exceptions;
|
||||
|
||||
/**
|
||||
* Provides a domain-level interface for broad capture
|
||||
* of all framework-related exceptions.
|
||||
*
|
||||
* catch (\CodeIgniter\Cache\Exceptions\ExceptionInterface) { ... }
|
||||
*/
|
||||
|
||||
interface ExceptionInterface
|
||||
{
|
||||
}
|
||||
186
system/Cache/Handlers/DummyHandler.php
Normal file
186
system/Cache/Handlers/DummyHandler.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Cache\Handlers;
|
||||
|
||||
use CodeIgniter\Cache\CacheInterface;
|
||||
|
||||
/**
|
||||
* Dummy cache handler
|
||||
*/
|
||||
|
||||
class DummyHandler implements CacheInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Takes care of any handler-specific setup that must be done.
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
// Nothing to see here...
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempts to fetch an item from the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Saves an item to the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
* @param mixed $value The data to save
|
||||
* @param integer $ttl Time To Live, in seconds (default 60)
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function save(string $key, $value, int $ttl = 60)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Deletes a specific item from the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(string $key)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic incrementation of a raw stored value.
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function increment(string $key, int $offset = 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic decrementation of a raw stored value.
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decrement(string $key, int $offset = 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will delete all items in the entire cache.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function clean()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns information on the entire cache.
|
||||
*
|
||||
* The information returned and the structure of the data
|
||||
* varies depending on the handler.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCacheInfo()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns detailed information about the specific item in the cache.
|
||||
*
|
||||
* @param string $key Cache item name.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetaData(string $key)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the driver is supported on this system.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSupported(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
531
system/Cache/Handlers/FileHandler.php
Normal file
531
system/Cache/Handlers/FileHandler.php
Normal file
@@ -0,0 +1,531 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Cache\Handlers;
|
||||
|
||||
use CodeIgniter\Cache\CacheInterface;
|
||||
use CodeIgniter\Cache\Exceptions\CacheException;
|
||||
|
||||
/**
|
||||
* File system cache handler
|
||||
*/
|
||||
class FileHandler implements CacheInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Prefixed to all cache names.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/**
|
||||
* Where to store cached files on the disk.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param type $config
|
||||
* @throws type
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
$path = ! empty($config->storePath) ? $config->storePath : WRITEPATH . 'cache';
|
||||
if (! is_really_writable($path))
|
||||
{
|
||||
throw CacheException::forUnableToWrite($path);
|
||||
}
|
||||
|
||||
$this->prefix = $config->prefix ?: '';
|
||||
$this->path = rtrim($path, '/') . '/';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes care of any handler-specific setup that must be done.
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
// Not to see here...
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempts to fetch an item from the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
$data = $this->getItem($key);
|
||||
|
||||
return is_array($data) ? $data['data'] : null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Saves an item to the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
* @param mixed $value The data to save
|
||||
* @param integer $ttl Time To Live, in seconds (default 60)
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function save(string $key, $value, int $ttl = 60)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
$contents = [
|
||||
'time' => time(),
|
||||
'ttl' => $ttl,
|
||||
'data' => $value,
|
||||
];
|
||||
|
||||
if ($this->writeFile($this->path . $key, serialize($contents)))
|
||||
{
|
||||
chmod($this->path . $key, 0640);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Deletes a specific item from the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(string $key)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
return is_file($this->path . $key) ? unlink($this->path . $key) : false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic incrementation of a raw stored value.
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function increment(string $key, int $offset = 1)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
$data = $this->getItem($key);
|
||||
|
||||
if ($data === false)
|
||||
{
|
||||
$data = [
|
||||
'data' => 0,
|
||||
'ttl' => 60,
|
||||
];
|
||||
}
|
||||
elseif (! is_int($data['data']))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$new_value = $data['data'] + $offset;
|
||||
|
||||
return $this->save($key, $new_value, $data['ttl']) ? $new_value : false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic decrementation of a raw stored value.
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decrement(string $key, int $offset = 1)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
$data = $this->getItem($key);
|
||||
|
||||
if ($data === false)
|
||||
{
|
||||
$data = [
|
||||
'data' => 0,
|
||||
'ttl' => 60,
|
||||
];
|
||||
}
|
||||
elseif (! is_int($data['data']))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$new_value = $data['data'] - $offset;
|
||||
|
||||
return $this->save($key, $new_value, $data['ttl']) ? $new_value : false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will delete all items in the entire cache.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function clean()
|
||||
{
|
||||
return $this->deleteFiles($this->path, false, true);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns information on the entire cache.
|
||||
*
|
||||
* The information returned and the structure of the data
|
||||
* varies depending on the handler.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCacheInfo()
|
||||
{
|
||||
return $this->getDirFileInfo($this->path);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns detailed information about the specific item in the cache.
|
||||
*
|
||||
* @param string $key Cache item name.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetaData(string $key)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
if (! is_file($this->path . $key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = @unserialize(file_get_contents($this->path . $key));
|
||||
|
||||
if (is_array($data))
|
||||
{
|
||||
$mtime = filemtime($this->path . $key);
|
||||
|
||||
if (! isset($data['ttl']))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return [
|
||||
'expire' => $mtime + $data['ttl'],
|
||||
'mtime' => $mtime,
|
||||
'data' => $data['data'],
|
||||
];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the driver is supported on this system.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSupported(): bool
|
||||
{
|
||||
return is_writable($this->path);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Does the heavy lifting of actually retrieving the file and
|
||||
* verifying it's age.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return boolean|mixed
|
||||
*/
|
||||
protected function getItem(string $key)
|
||||
{
|
||||
if (! is_file($this->path . $key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = unserialize(file_get_contents($this->path . $key));
|
||||
|
||||
if ($data['ttl'] > 0 && time() > $data['time'] + $data['ttl'])
|
||||
{
|
||||
unlink($this->path . $key);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------
|
||||
// SUPPORT METHODS FOR FILES
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Writes a file to disk, or returns false if not successful.
|
||||
*
|
||||
* @param $path
|
||||
* @param $data
|
||||
* @param string $mode
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function writeFile($path, $data, $mode = 'wb')
|
||||
{
|
||||
if (($fp = @fopen($path, $mode)) === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
flock($fp, LOCK_EX);
|
||||
|
||||
for ($result = $written = 0, $length = strlen($data); $written < $length; $written += $result)
|
||||
{
|
||||
if (($result = fwrite($fp, substr($data, $written))) === false)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
flock($fp, LOCK_UN);
|
||||
fclose($fp);
|
||||
|
||||
return is_int($result);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Delete Files
|
||||
*
|
||||
* Deletes all files contained in the supplied directory path.
|
||||
* Files must be writable or owned by the system in order to be deleted.
|
||||
* If the second parameter is set to TRUE, any directories contained
|
||||
* within the supplied base directory will be nuked as well.
|
||||
*
|
||||
* @param string $path File path
|
||||
* @param boolean $del_dir Whether to delete any directories found in the path
|
||||
* @param boolean $htdocs Whether to skip deleting .htaccess and index page files
|
||||
* @param integer $_level Current directory depth level (default: 0; internal use only)
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function deleteFiles(string $path, bool $del_dir = false, bool $htdocs = false, int $_level = 0): bool
|
||||
{
|
||||
// Trim the trailing slash
|
||||
$path = rtrim($path, '/\\');
|
||||
|
||||
if (! $current_dir = @opendir($path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while (false !== ($filename = @readdir($current_dir)))
|
||||
{
|
||||
if ($filename !== '.' && $filename !== '..')
|
||||
{
|
||||
if (is_dir($path . DIRECTORY_SEPARATOR . $filename) && $filename[0] !== '.')
|
||||
{
|
||||
$this->deleteFiles($path . DIRECTORY_SEPARATOR . $filename, $del_dir, $htdocs, $_level + 1);
|
||||
}
|
||||
elseif ($htdocs !== true || ! preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename))
|
||||
{
|
||||
@unlink($path . DIRECTORY_SEPARATOR . $filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir($current_dir);
|
||||
|
||||
return ($del_dir === true && $_level > 0) ? @rmdir($path) : true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get Directory File Information
|
||||
*
|
||||
* Reads the specified directory and builds an array containing the filenames,
|
||||
* filesize, dates, and permissions
|
||||
*
|
||||
* Any sub-folders contained within the specified path are read as well.
|
||||
*
|
||||
* @param string $source_dir Path to source
|
||||
* @param boolean $top_level_only Look only at the top level directory specified?
|
||||
* @param boolean $_recursion Internal variable to determine recursion status - do not use in calls
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
protected function getDirFileInfo(string $source_dir, bool $top_level_only = true, bool $_recursion = false)
|
||||
{
|
||||
static $_filedata = [];
|
||||
$relative_path = $source_dir;
|
||||
|
||||
if ($fp = @opendir($source_dir))
|
||||
{
|
||||
// reset the array and make sure $source_dir has a trailing slash on the initial call
|
||||
if ($_recursion === false)
|
||||
{
|
||||
$_filedata = [];
|
||||
$source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
// Used to be foreach (scandir($source_dir, 1) as $file), but scandir() is simply not as fast
|
||||
while (false !== ($file = readdir($fp)))
|
||||
{
|
||||
if (is_dir($source_dir . $file) && $file[0] !== '.' && $top_level_only === false)
|
||||
{
|
||||
$this->getDirFileInfo($source_dir . $file . DIRECTORY_SEPARATOR, $top_level_only, true);
|
||||
}
|
||||
elseif ($file[0] !== '.')
|
||||
{
|
||||
$_filedata[$file] = $this->getFileInfo($source_dir . $file);
|
||||
$_filedata[$file]['relative_path'] = $relative_path;
|
||||
}
|
||||
}
|
||||
|
||||
closedir($fp);
|
||||
|
||||
return $_filedata;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get File Info
|
||||
*
|
||||
* Given a file and path, returns the name, path, size, date modified
|
||||
* Second parameter allows you to explicitly declare what information you want returned
|
||||
* Options are: name, server_path, size, date, readable, writable, executable, fileperms
|
||||
* Returns FALSE if the file cannot be found.
|
||||
*
|
||||
* @param string $file Path to file
|
||||
* @param mixed $returned_values Array or comma separated string of information returned
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
protected function getFileInfo(string $file, $returned_values = ['name', 'server_path', 'size', 'date'])
|
||||
{
|
||||
if (! is_file($file))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_string($returned_values))
|
||||
{
|
||||
$returned_values = explode(',', $returned_values);
|
||||
}
|
||||
|
||||
foreach ($returned_values as $key)
|
||||
{
|
||||
switch ($key)
|
||||
{
|
||||
case 'name':
|
||||
$fileInfo['name'] = basename($file);
|
||||
break;
|
||||
case 'server_path':
|
||||
$fileInfo['server_path'] = $file;
|
||||
break;
|
||||
case 'size':
|
||||
$fileInfo['size'] = filesize($file);
|
||||
break;
|
||||
case 'date':
|
||||
$fileInfo['date'] = filemtime($file);
|
||||
break;
|
||||
case 'readable':
|
||||
$fileInfo['readable'] = is_readable($file);
|
||||
break;
|
||||
case 'writable':
|
||||
$fileInfo['writable'] = is_writable($file);
|
||||
break;
|
||||
case 'executable':
|
||||
$fileInfo['executable'] = is_executable($file);
|
||||
break;
|
||||
case 'fileperms':
|
||||
$fileInfo['fileperms'] = fileperms($file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $fileInfo;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
389
system/Cache/Handlers/MemcachedHandler.php
Normal file
389
system/Cache/Handlers/MemcachedHandler.php
Normal file
@@ -0,0 +1,389 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Cache\Handlers;
|
||||
|
||||
use CodeIgniter\Cache\CacheInterface;
|
||||
use CodeIgniter\Exceptions\CriticalError;
|
||||
|
||||
/**
|
||||
* Mamcached cache handler
|
||||
*/
|
||||
class MemcachedHandler implements CacheInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Prefixed to all cache names.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/**
|
||||
* The memcached object
|
||||
*
|
||||
* @var \Memcached|\Memcache
|
||||
*/
|
||||
protected $memcached;
|
||||
|
||||
/**
|
||||
* Memcached Configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 11211,
|
||||
'weight' => 1,
|
||||
'raw' => false,
|
||||
];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param type $config
|
||||
* @throws type
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
$config = (array)$config;
|
||||
$this->prefix = $config['prefix'] ?? '';
|
||||
|
||||
if (! empty($config))
|
||||
{
|
||||
$this->config = array_merge($this->config, $config['memcached']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class destructor
|
||||
*
|
||||
* Closes the connection to Memcache(d) if present.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->memcached instanceof \Memcached)
|
||||
{
|
||||
$this->memcached->quit();
|
||||
}
|
||||
elseif ($this->memcached instanceof \Memcache)
|
||||
{
|
||||
$this->memcached->close();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes care of any handler-specific setup that must be done.
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
// Try to connect to Memcache or Memcached, if an issue occurs throw a CriticalError exception,
|
||||
// so that the CacheFactory can attempt to initiate the next cache handler.
|
||||
try
|
||||
{
|
||||
if (class_exists('\Memcached'))
|
||||
{
|
||||
// Create new instance of \Memcached
|
||||
$this->memcached = new \Memcached();
|
||||
if ($this->config['raw'])
|
||||
{
|
||||
$this->memcached->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
|
||||
}
|
||||
|
||||
// Add server
|
||||
$this->memcached->addServer(
|
||||
$this->config['host'], $this->config['port'], $this->config['weight']
|
||||
);
|
||||
|
||||
// attempt to get status of servers
|
||||
$stats = $this->memcached->getStats();
|
||||
|
||||
// $stats should be an associate array with a key in the format of host:port.
|
||||
// If it doesn't have the key, we know the server is not working as expected.
|
||||
if (! isset($stats[$this->config['host'] . ':' . $this->config['port']]))
|
||||
{
|
||||
throw new CriticalError('Cache: Memcached connection failed.');
|
||||
}
|
||||
}
|
||||
elseif (class_exists('\Memcache'))
|
||||
{
|
||||
// Create new instance of \Memcache
|
||||
$this->memcached = new \Memcache();
|
||||
|
||||
// Check if we can connect to the server
|
||||
$can_connect = $this->memcached->connect(
|
||||
$this->config['host'], $this->config['port']
|
||||
);
|
||||
|
||||
// If we can't connect, throw a CriticalError exception
|
||||
if ($can_connect === false)
|
||||
{
|
||||
throw new CriticalError('Cache: Memcache connection failed.');
|
||||
}
|
||||
|
||||
// Add server, third parameter is persistence and defaults to TRUE.
|
||||
$this->memcached->addServer(
|
||||
$this->config['host'], $this->config['port'], true, $this->config['weight']
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CriticalError('Cache: Not support Memcache(d) extension.');
|
||||
}
|
||||
}
|
||||
catch (CriticalError $e)
|
||||
{
|
||||
// If a CriticalError exception occurs, throw it up.
|
||||
throw $e;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// If an \Exception occurs, convert it into a CriticalError exception and throw it.
|
||||
throw new CriticalError('Cache: Memcache(d) connection refused (' . $e->getMessage() . ').');
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempts to fetch an item from the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
if ($this->memcached instanceof \Memcached)
|
||||
{
|
||||
$data = $this->memcached->get($key);
|
||||
|
||||
// check for unmatched key
|
||||
if ($this->memcached->getResultCode() === \Memcached::RES_NOTFOUND)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
elseif ($this->memcached instanceof \Memcache)
|
||||
{
|
||||
$flags = false;
|
||||
$data = $this->memcached->get($key, $flags);
|
||||
|
||||
// check for unmatched key (i.e. $flags is untouched)
|
||||
if ($flags === false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return is_array($data) ? $data[0] : $data;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Saves an item to the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
* @param mixed $value The data to save
|
||||
* @param integer $ttl Time To Live, in seconds (default 60)
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function save(string $key, $value, int $ttl = 60)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
if (! $this->config['raw'])
|
||||
{
|
||||
$value = [
|
||||
$value,
|
||||
time(),
|
||||
$ttl,
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->memcached instanceof \Memcached)
|
||||
{
|
||||
return $this->memcached->set($key, $value, $ttl);
|
||||
}
|
||||
elseif ($this->memcached instanceof \Memcache)
|
||||
{
|
||||
return $this->memcached->set($key, $value, 0, $ttl);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Deletes a specific item from the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(string $key)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
return $this->memcached->delete($key);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic incrementation of a raw stored value.
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function increment(string $key, int $offset = 1)
|
||||
{
|
||||
if (! $this->config['raw'])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
return $this->memcached->increment($key, $offset, $offset, 60);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic decrementation of a raw stored value.
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decrement(string $key, int $offset = 1)
|
||||
{
|
||||
if (! $this->config['raw'])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
//FIXME: third parameter isn't other handler actions.
|
||||
return $this->memcached->decrement($key, $offset, $offset, 60);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will delete all items in the entire cache.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function clean()
|
||||
{
|
||||
return $this->memcached->flush();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns information on the entire cache.
|
||||
*
|
||||
* The information returned and the structure of the data
|
||||
* varies depending on the handler.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCacheInfo()
|
||||
{
|
||||
return $this->memcached->getStats();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns detailed information about the specific item in the cache.
|
||||
*
|
||||
* @param string $key Cache item name.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetaData(string $key)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
$stored = $this->memcached->get($key);
|
||||
|
||||
// if not an array, don't try to count for PHP7.2
|
||||
if (! is_array($stored) || count($stored) !== 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
list($data, $time, $ttl) = $stored;
|
||||
|
||||
return [
|
||||
'expire' => $time + $ttl,
|
||||
'mtime' => $time,
|
||||
'data' => $data,
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the driver is supported on this system.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSupported(): bool
|
||||
{
|
||||
return (extension_loaded('memcached') || extension_loaded('memcache'));
|
||||
}
|
||||
|
||||
}
|
||||
306
system/Cache/Handlers/PredisHandler.php
Normal file
306
system/Cache/Handlers/PredisHandler.php
Normal file
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Cache\Handlers;
|
||||
|
||||
use CodeIgniter\Cache\CacheInterface;
|
||||
use CodeIgniter\Exceptions\CriticalError;
|
||||
|
||||
/**
|
||||
* Predis cache handler
|
||||
*/
|
||||
class PredisHandler implements CacheInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Prefixed to all cache names.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/**
|
||||
* Default config
|
||||
*
|
||||
* @static
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [
|
||||
'scheme' => 'tcp',
|
||||
'host' => '127.0.0.1',
|
||||
'password' => null,
|
||||
'port' => 6379,
|
||||
'timeout' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* Predis connection
|
||||
*
|
||||
* @var Predis
|
||||
*/
|
||||
protected $redis;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param type $config
|
||||
* @throws type
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->prefix = $config->prefix ?: '';
|
||||
|
||||
if (isset($config->redis))
|
||||
{
|
||||
$this->config = array_merge($this->config, $config->redis);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes care of any handler-specific setup that must be done.
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
// Try to connect to Redis, if an issue occurs throw a CriticalError exception,
|
||||
// so that the CacheFactory can attempt to initiate the next cache handler.
|
||||
try
|
||||
{
|
||||
// Create a new instance of Predis\Client
|
||||
$this->redis = new \Predis\Client($this->config, ['prefix' => $this->prefix]);
|
||||
|
||||
// Check if the connection is valid by trying to get the time.
|
||||
$this->redis->time();
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// thrown if can't connect to redis server.
|
||||
throw new CriticalError('Cache: Predis connection refused (' . $e->getMessage() . ').');
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempts to fetch an item from the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key)
|
||||
{
|
||||
$data = array_combine([
|
||||
'__ci_type',
|
||||
'__ci_value',
|
||||
], $this->redis->hmget($key, ['__ci_type', '__ci_value'])
|
||||
);
|
||||
|
||||
if (! isset($data['__ci_type'], $data['__ci_value']) || $data['__ci_value'] === false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch ($data['__ci_type'])
|
||||
{
|
||||
case 'array':
|
||||
case 'object':
|
||||
return unserialize($data['__ci_value']);
|
||||
case 'boolean':
|
||||
case 'integer':
|
||||
case 'double': // Yes, 'double' is returned and NOT 'float'
|
||||
case 'string':
|
||||
case 'NULL':
|
||||
return settype($data['__ci_value'], $data['__ci_type']) ? $data['__ci_value'] : null;
|
||||
case 'resource':
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Saves an item to the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
* @param mixed $value The data to save
|
||||
* @param integer $ttl Time To Live, in seconds (default 60)
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function save(string $key, $value, int $ttl = 60)
|
||||
{
|
||||
switch ($data_type = gettype($value))
|
||||
{
|
||||
case 'array':
|
||||
case 'object':
|
||||
$value = serialize($value);
|
||||
break;
|
||||
case 'boolean':
|
||||
case 'integer':
|
||||
case 'double': // Yes, 'double' is returned and NOT 'float'
|
||||
case 'string':
|
||||
case 'NULL':
|
||||
break;
|
||||
case 'resource':
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->redis->hmset($key, ['__ci_type' => $data_type, '__ci_value' => $value]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->redis->expireat($key, time() + $ttl);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Deletes a specific item from the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(string $key)
|
||||
{
|
||||
return ($this->redis->del($key) === 1);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic incrementation of a raw stored value.
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function increment(string $key, int $offset = 1)
|
||||
{
|
||||
return $this->redis->hincrby($key, 'data', $offset);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic decrementation of a raw stored value.
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decrement(string $key, int $offset = 1)
|
||||
{
|
||||
return $this->redis->hincrby($key, 'data', -$offset);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will delete all items in the entire cache.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function clean()
|
||||
{
|
||||
return $this->redis->flushdb();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns information on the entire cache.
|
||||
*
|
||||
* The information returned and the structure of the data
|
||||
* varies depending on the handler.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCacheInfo()
|
||||
{
|
||||
return $this->redis->info();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns detailed information about the specific item in the cache.
|
||||
*
|
||||
* @param string $key Cache item name.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetaData(string $key)
|
||||
{
|
||||
$data = array_combine(['__ci_value'], $this->redis->hmget($key, ['__ci_value']));
|
||||
|
||||
if (isset($data['__ci_value']) && $data['__ci_value'] !== false)
|
||||
{
|
||||
return [
|
||||
'expire' => time() + $this->redis->ttl($key),
|
||||
'data' => $data['__ci_value'],
|
||||
];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the driver is supported on this system.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSupported(): bool
|
||||
{
|
||||
return class_exists('\Predis\Client');
|
||||
}
|
||||
|
||||
}
|
||||
355
system/Cache/Handlers/RedisHandler.php
Normal file
355
system/Cache/Handlers/RedisHandler.php
Normal file
@@ -0,0 +1,355 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Cache\Handlers;
|
||||
|
||||
use CodeIgniter\Cache\CacheInterface;
|
||||
use CodeIgniter\Exceptions\CriticalError;
|
||||
|
||||
/**
|
||||
* Redis cache handler
|
||||
*/
|
||||
class RedisHandler implements CacheInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Prefixed to all cache names.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/**
|
||||
* Default config
|
||||
*
|
||||
* @static
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [
|
||||
'host' => '127.0.0.1',
|
||||
'password' => null,
|
||||
'port' => 6379,
|
||||
'timeout' => 0,
|
||||
'database' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* Redis connection
|
||||
*
|
||||
* @var Redis
|
||||
*/
|
||||
protected $redis;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param type $config
|
||||
* @throws type
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
$config = (array)$config;
|
||||
$this->prefix = $config['prefix'] ?? '';
|
||||
|
||||
if (! empty($config))
|
||||
{
|
||||
$this->config = array_merge($this->config, $config['redis']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class destructor
|
||||
*
|
||||
* Closes the connection to Memcache(d) if present.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->redis)
|
||||
{
|
||||
$this->redis->close();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes care of any handler-specific setup that must be done.
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
$config = $this->config;
|
||||
|
||||
$this->redis = new \Redis();
|
||||
|
||||
// Try to connect to Redis, if an issue occurs throw a CriticalError exception,
|
||||
// so that the CacheFactory can attempt to initiate the next cache handler.
|
||||
try
|
||||
{
|
||||
// Note:: If Redis is your primary cache choice, and it is "offline", every page load will end up been delayed by the timeout duration.
|
||||
// I feel like some sort of temporary flag should be set, to indicate that we think Redis is "offline", allowing us to bypass the timeout for a set period of time.
|
||||
|
||||
if (! $this->redis->connect($config['host'], ($config['host'][0] === '/' ? 0 : $config['port']), $config['timeout']))
|
||||
{
|
||||
// Note:: I'm unsure if log_message() is necessary, however I'm not 100% comfortable removing it.
|
||||
log_message('error', 'Cache: Redis connection failed. Check your configuration.');
|
||||
throw new CriticalError('Cache: Redis connection failed. Check your configuration.');
|
||||
}
|
||||
|
||||
if (isset($config['password']) && ! $this->redis->auth($config['password']))
|
||||
{
|
||||
log_message('error', 'Cache: Redis authentication failed.');
|
||||
throw new CriticalError('Cache: Redis authentication failed.');
|
||||
}
|
||||
|
||||
if (isset($config['database']) && ! $this->redis->select($config['database']))
|
||||
{
|
||||
log_message('error', 'Cache: Redis select database failed.');
|
||||
throw new CriticalError('Cache: Redis select database failed.');
|
||||
}
|
||||
}
|
||||
catch (\RedisException $e)
|
||||
{
|
||||
// $this->redis->connect() can sometimes throw a RedisException.
|
||||
// We need to convert the exception into a CriticalError exception and throw it.
|
||||
throw new CriticalError('Cache: RedisException occurred with message (' . $e->getMessage() . ').');
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempts to fetch an item from the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
$data = $this->redis->hMGet($key, ['__ci_type', '__ci_value']);
|
||||
|
||||
if (! isset($data['__ci_type'], $data['__ci_value']) || $data['__ci_value'] === false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch ($data['__ci_type'])
|
||||
{
|
||||
case 'array':
|
||||
case 'object':
|
||||
return unserialize($data['__ci_value']);
|
||||
case 'boolean':
|
||||
case 'integer':
|
||||
case 'double': // Yes, 'double' is returned and NOT 'float'
|
||||
case 'string':
|
||||
case 'NULL':
|
||||
return settype($data['__ci_value'], $data['__ci_type']) ? $data['__ci_value'] : null;
|
||||
case 'resource':
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Saves an item to the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
* @param mixed $value The data to save
|
||||
* @param integer $ttl Time To Live, in seconds (default 60)
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function save(string $key, $value, int $ttl = 60)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
switch ($data_type = gettype($value))
|
||||
{
|
||||
case 'array':
|
||||
case 'object':
|
||||
$value = serialize($value);
|
||||
break;
|
||||
case 'boolean':
|
||||
case 'integer':
|
||||
case 'double': // Yes, 'double' is returned and NOT 'float'
|
||||
case 'string':
|
||||
case 'NULL':
|
||||
break;
|
||||
case 'resource':
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->redis->hMSet($key, ['__ci_type' => $data_type, '__ci_value' => $value]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
elseif ($ttl)
|
||||
{
|
||||
$this->redis->expireAt($key, time() + $ttl);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Deletes a specific item from the cache store.
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(string $key)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
return ($this->redis->del($key) === 1);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic incrementation of a raw stored value.
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function increment(string $key, int $offset = 1)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
return $this->redis->hIncrBy($key, 'data', $offset);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic decrementation of a raw stored value.
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decrement(string $key, int $offset = 1)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
return $this->redis->hIncrBy($key, 'data', -$offset);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will delete all items in the entire cache.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function clean()
|
||||
{
|
||||
return $this->redis->flushDB();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns information on the entire cache.
|
||||
*
|
||||
* The information returned and the structure of the data
|
||||
* varies depending on the handler.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCacheInfo()
|
||||
{
|
||||
return $this->redis->info();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns detailed information about the specific item in the cache.
|
||||
*
|
||||
* @param string $key Cache item name.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetaData(string $key)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
$value = $this->get($key);
|
||||
|
||||
if ($value !== null)
|
||||
{
|
||||
$time = time();
|
||||
return [
|
||||
'expire' => $time + $this->redis->ttl($key),
|
||||
'mtime' => $time,
|
||||
'data' => $value,
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the driver is supported on this system.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSupported(): bool
|
||||
{
|
||||
return extension_loaded('redis');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
263
system/Cache/Handlers/WincacheHandler.php
Normal file
263
system/Cache/Handlers/WincacheHandler.php
Normal file
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Cache\Handlers;
|
||||
|
||||
use CodeIgniter\Cache\CacheInterface;
|
||||
|
||||
/**
|
||||
* Cache handler for WinCache from Microsoft & IIS.
|
||||
* Windows-only, so not testable on travis-ci.
|
||||
* Unusable methods flagged for code coverage ignoring.
|
||||
*/
|
||||
class WincacheHandler implements CacheInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Prefixed to all cache names.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param type $config
|
||||
* @throws type
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->prefix = $config->prefix ?: '';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes care of any handler-specific setup that must be done.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
// Nothing to see here...
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempts to fetch an item from the cache store.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
$success = false;
|
||||
$data = wincache_ucache_get($key, $success);
|
||||
|
||||
// Success returned by reference from wincache_ucache_get()
|
||||
return ($success) ? $data : null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Saves an item to the cache store.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
* @param mixed $value The data to save
|
||||
* @param integer $ttl Time To Live, in seconds (default 60)
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function save(string $key, $value, int $ttl = 60)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
return wincache_ucache_set($key, $value, $ttl);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Deletes a specific item from the cache store.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $key Cache item name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(string $key)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
return wincache_ucache_delete($key);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic incrementation of a raw stored value.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function increment(string $key, int $offset = 1)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
$success = false;
|
||||
$value = wincache_ucache_inc($key, $offset, $success);
|
||||
|
||||
return ($success === true) ? $value : false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs atomic decrementation of a raw stored value.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $key Cache ID
|
||||
* @param integer $offset Step/value to increase by
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decrement(string $key, int $offset = 1)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
$success = false;
|
||||
$value = wincache_ucache_dec($key, $offset, $success);
|
||||
|
||||
return ($success === true) ? $value : false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will delete all items in the entire cache.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function clean()
|
||||
{
|
||||
return wincache_ucache_clear();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns information on the entire cache.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* The information returned and the structure of the data
|
||||
* varies depending on the handler.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCacheInfo()
|
||||
{
|
||||
return wincache_ucache_info(true);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns detailed information about the specific item in the cache.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @param string $key Cache item name.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetaData(string $key)
|
||||
{
|
||||
$key = $this->prefix . $key;
|
||||
|
||||
if ($stored = wincache_ucache_info(false, $key))
|
||||
{
|
||||
$age = $stored['ucache_entries'][1]['age_seconds'];
|
||||
$ttl = $stored['ucache_entries'][1]['ttl_seconds'];
|
||||
$hitcount = $stored['ucache_entries'][1]['hitcount'];
|
||||
|
||||
return [
|
||||
'expire' => $ttl - $age,
|
||||
'hitcount' => $hitcount,
|
||||
'age' => $age,
|
||||
'ttl' => $ttl,
|
||||
];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the driver is supported on this system.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSupported(): bool
|
||||
{
|
||||
return (extension_loaded('wincache') && ini_get('wincache.ucenabled'));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
1053
system/CodeIgniter.php
Normal file
1053
system/CodeIgniter.php
Normal file
File diff suppressed because it is too large
Load Diff
188
system/Commands/Database/CreateMigration.php
Normal file
188
system/Commands/Database/CreateMigration.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Config\Autoload;
|
||||
use Config\Migrations;
|
||||
|
||||
/**
|
||||
* Creates a new migration file.
|
||||
*
|
||||
* @package CodeIgniter\Commands
|
||||
*/
|
||||
class CreateMigration extends BaseCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate:create';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Creates a new migration file.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate:create [migration_name] [Options]';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [
|
||||
'migration_name' => 'The migration file name',
|
||||
];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
'-n' => 'Set migration namespace',
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates a new migration file with the current timestamp.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function run(array $params = [])
|
||||
{
|
||||
helper('inflector');
|
||||
$name = array_shift($params);
|
||||
|
||||
if (empty($name))
|
||||
{
|
||||
$name = CLI::prompt(lang('Migrations.nameMigration'));
|
||||
}
|
||||
|
||||
if (empty($name))
|
||||
{
|
||||
CLI::error(lang('Migrations.badCreateName'));
|
||||
return;
|
||||
}
|
||||
|
||||
$ns = $params['-n'] ?? CLI::getOption('n');
|
||||
$homepath = APPPATH;
|
||||
|
||||
if (! empty($ns))
|
||||
{
|
||||
// Get all namespaces from PSR4 paths.
|
||||
$config = new Autoload();
|
||||
$namespaces = $config->psr4;
|
||||
|
||||
foreach ($namespaces as $namespace => $path)
|
||||
{
|
||||
if ($namespace === $ns)
|
||||
{
|
||||
$homepath = realpath($path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$ns = 'App';
|
||||
}
|
||||
|
||||
// Always use UTC/GMT so global teams can work together
|
||||
$config = config('Migrations');
|
||||
$fileName = gmdate($config->timestampFormat) . $name;
|
||||
|
||||
// full path
|
||||
$path = $homepath . '/Database/Migrations/' . $fileName . '.php';
|
||||
|
||||
// Class name should be pascal case now (camel case with upper first letter)
|
||||
$name = pascalize($name);
|
||||
|
||||
$template = <<<EOD
|
||||
<?php namespace $ns\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class {name} extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
EOD;
|
||||
$template = str_replace('{name}', $name, $template);
|
||||
|
||||
helper('filesystem');
|
||||
if (! write_file($path, $template))
|
||||
{
|
||||
CLI::error(lang('Migrations.writeError'));
|
||||
return;
|
||||
}
|
||||
|
||||
CLI::write('Created file: ' . CLI::color(str_replace($homepath, $ns, $path), 'green'));
|
||||
}
|
||||
|
||||
}
|
||||
167
system/Commands/Database/Migrate.php
Normal file
167
system/Commands/Database/Migrate.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Config\Services;
|
||||
|
||||
/**
|
||||
* Runs all new migrations.
|
||||
*
|
||||
* @package CodeIgniter\Commands
|
||||
*/
|
||||
class Migrate extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Locates and runs all new migrations against the database.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate [options]';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
'-n' => 'Set migration namespace',
|
||||
'-g' => 'Set database group',
|
||||
'-all' => 'Set for all namespaces, will ignore (-n) option',
|
||||
];
|
||||
|
||||
/**
|
||||
* Ensures that all migrations have been run.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function run(array $params = [])
|
||||
{
|
||||
$runner = Services::migrations();
|
||||
$runner->clearCliMessages();
|
||||
|
||||
CLI::write(lang('Migrations.latest'), 'yellow');
|
||||
|
||||
$namespace = $params['-n'] ?? CLI::getOption('n');
|
||||
$group = $params['-g'] ?? CLI::getOption('g');
|
||||
|
||||
try
|
||||
{
|
||||
// Check for 'all' namespaces
|
||||
if ($this->isAllNamespace($params))
|
||||
{
|
||||
$runner->setNamespace(null);
|
||||
}
|
||||
// Check for a specified namespace
|
||||
elseif ($namespace)
|
||||
{
|
||||
$runner->setNamespace($namespace);
|
||||
}
|
||||
|
||||
if (! $runner->latest($group))
|
||||
{
|
||||
CLI::write(lang('Migrations.generalFault'), 'red');
|
||||
}
|
||||
|
||||
$messages = $runner->getCliMessages();
|
||||
foreach ($messages as $message)
|
||||
{
|
||||
CLI::write($message);
|
||||
}
|
||||
|
||||
CLI::write('Done');
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
$this->showError($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To migrate all namespaces to the latest migration
|
||||
*
|
||||
* Demo:
|
||||
* 1. command line: php spark migrate:latest -all
|
||||
* 2. command file: $this->call('migrate:latest', ['-g' => 'test','-all']);
|
||||
*
|
||||
* @param array $params
|
||||
* @return boolean
|
||||
*/
|
||||
private function isAllNamespace(array $params): bool
|
||||
{
|
||||
if (array_search('-all', $params) !== false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return ! is_null(CLI::getOption('all'));
|
||||
}
|
||||
|
||||
}
|
||||
112
system/Commands/Database/MigrateRefresh.php
Normal file
112
system/Commands/Database/MigrateRefresh.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
|
||||
/**
|
||||
* Does a rollback followed by a latest to refresh the current state
|
||||
* of the database.
|
||||
*
|
||||
* @package CodeIgniter\Commands
|
||||
*/
|
||||
class MigrateRefresh extends BaseCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate:refresh';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Does a rollback followed by a latest to refresh the current state of the database.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate:refresh [Options]';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
'-n' => 'Set migration namespace',
|
||||
'-g' => 'Set database group',
|
||||
'-all' => 'Set latest for all namespace, will ignore (-n) option',
|
||||
];
|
||||
|
||||
/**
|
||||
* Does a rollback followed by a latest to refresh the current state
|
||||
* of the database.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function run(array $params = [])
|
||||
{
|
||||
$this->call('migrate:rollback', ['-b' => 0]);
|
||||
$this->call('migrate');
|
||||
}
|
||||
|
||||
}
|
||||
142
system/Commands/Database/MigrateRollback.php
Normal file
142
system/Commands/Database/MigrateRollback.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Config\Services;
|
||||
use Config\Autoload;
|
||||
|
||||
/**
|
||||
* Runs all of the migrations in reverse order, until they have
|
||||
* all been un-applied.
|
||||
*
|
||||
* @package CodeIgniter\Commands
|
||||
*/
|
||||
class MigrateRollback extends BaseCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate:rollback';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Runs the "down" method for all migrations in the last batch.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate:rollback [Options]';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
'-b' => 'Specify a batch to roll back to; e.g. "3" to return to batch #3 or "-2" to roll back twice',
|
||||
'-g' => 'Set database group',
|
||||
];
|
||||
|
||||
/**
|
||||
* Runs all of the migrations in reverse order, until they have
|
||||
* all been un-applied.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function run(array $params = [])
|
||||
{
|
||||
$runner = Services::migrations();
|
||||
|
||||
$group = $params['-g'] ?? CLI::getOption('g');
|
||||
|
||||
if (! is_null($group))
|
||||
{
|
||||
$runner->setGroup($group);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$batch = $params['-b'] ?? CLI::getOption('b') ?? $runner->getLastBatch() - 1;
|
||||
CLI::write(lang('Migrations.rollingBack') . ' ' . $batch, 'yellow');
|
||||
|
||||
if (! $runner->regress($batch))
|
||||
{
|
||||
CLI::write(lang('Migrations.generalFault'), 'red');
|
||||
}
|
||||
|
||||
$messages = $runner->getCliMessages();
|
||||
foreach ($messages as $message)
|
||||
{
|
||||
CLI::write($message);
|
||||
}
|
||||
|
||||
CLI::write('Done');
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
$this->showError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
181
system/Commands/Database/MigrateStatus.php
Normal file
181
system/Commands/Database/MigrateStatus.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Config\Services;
|
||||
use Config\Autoload;
|
||||
|
||||
/**
|
||||
* Displays a list of all migrations and whether they've been run or not.
|
||||
*
|
||||
* @package CodeIgniter\Commands
|
||||
*/
|
||||
class MigrateStatus extends BaseCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate:status';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Displays a list of all migrations and whether they\'ve been run or not.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate:status [Options]';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
'-g' => 'Set database group',
|
||||
];
|
||||
|
||||
/**
|
||||
* Namespaces to ignore when looking for migrations.
|
||||
*
|
||||
* @var type
|
||||
*/
|
||||
protected $ignoredNamespaces = [
|
||||
'CodeIgniter',
|
||||
'Config',
|
||||
'Tests\Support',
|
||||
];
|
||||
|
||||
/**
|
||||
* Displays a list of all migrations and whether they've been run or not.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function run(array $params = [])
|
||||
{
|
||||
$runner = Services::migrations();
|
||||
|
||||
$group = $params['-g'] ?? CLI::getOption('g');
|
||||
|
||||
if (! is_null($group))
|
||||
{
|
||||
$runner->setGroup($group);
|
||||
}
|
||||
|
||||
// Get all namespaces from PSR4 paths.
|
||||
$config = new Autoload();
|
||||
$namespaces = $config->psr4;
|
||||
|
||||
// Loop for all $namespaces
|
||||
foreach ($namespaces as $namespace => $path)
|
||||
{
|
||||
if (in_array($namespace, $this->ignoredNamespaces))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$runner->setNamespace($namespace);
|
||||
$migrations = $runner->findMigrations();
|
||||
$history = $runner->getHistory();
|
||||
|
||||
CLI::write($namespace);
|
||||
|
||||
if (empty($migrations))
|
||||
{
|
||||
CLI::error(lang('Migrations.noneFound'));
|
||||
continue;
|
||||
}
|
||||
|
||||
ksort($migrations);
|
||||
|
||||
$max = 0;
|
||||
foreach ($migrations as $version => $migration)
|
||||
{
|
||||
$file = substr($migration->name, strpos($migration->name, $version . '_'));
|
||||
$migrations[$version]->name = $file;
|
||||
|
||||
$max = max($max, strlen($file));
|
||||
}
|
||||
|
||||
CLI::write(' ' . str_pad(lang('Migrations.filename'), $max + 4) . lang('Migrations.on'), 'yellow');
|
||||
|
||||
foreach ($migrations as $uid => $migration)
|
||||
{
|
||||
$date = '';
|
||||
foreach ($history as $row)
|
||||
{
|
||||
if ($runner->getObjectUid($row) !== $uid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$date = date('Y-m-d H:i:s', $row->time);
|
||||
}
|
||||
CLI::write(str_pad(' ' . $migration->name, $max + 6) . ($date ? $date : '---'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
133
system/Commands/Database/Seed.php
Normal file
133
system/Commands/Database/Seed.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Database\Seeder;
|
||||
|
||||
/**
|
||||
* Runs the specified Seeder file to populate the database
|
||||
* with some data.
|
||||
*
|
||||
* @package CodeIgniter\Commands
|
||||
*/
|
||||
class Seed extends BaseCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'db:seed';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Runs the specified seeder to populate known data into the database.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'db:seed [seeder_name]';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [
|
||||
'seeder_name' => 'The seeder name to run',
|
||||
];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* Runs all of the migrations in reverse order, until they have
|
||||
* all been un-applied.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function run(array $params = [])
|
||||
{
|
||||
$seeder = new Seeder(new \Config\Database());
|
||||
|
||||
$seedName = array_shift($params);
|
||||
|
||||
if (empty($seedName))
|
||||
{
|
||||
$seedName = CLI::prompt(lang('Migrations.migSeeder'), 'DatabaseSeeder');
|
||||
}
|
||||
|
||||
if (empty($seedName))
|
||||
{
|
||||
CLI::error(lang('Migrations.migMissingSeeder'));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$seeder->call($seedName);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
$this->showError($e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
121
system/Commands/Help.php
Normal file
121
system/Commands/Help.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
|
||||
/**
|
||||
* CI Help command for the spark script.
|
||||
*
|
||||
* Lists the basic usage information for the spark script,
|
||||
* and provides a way to list help for other commands.
|
||||
*
|
||||
* @package CodeIgniter\Commands
|
||||
*/
|
||||
class Help extends BaseCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'help';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Displays basic usage information.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'help command_name';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [
|
||||
'command_name' => 'The command name [default: "help"]',
|
||||
];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Displays the help for the spark cli script itself.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$command = array_shift($params);
|
||||
if (is_null($command))
|
||||
{
|
||||
$command = 'help';
|
||||
}
|
||||
|
||||
$commands = $this->commands->getCommands();
|
||||
$class = new $commands[$command]['class']($this->logger, $this->commands);
|
||||
|
||||
$class->showHelp();
|
||||
}
|
||||
|
||||
}
|
||||
197
system/Commands/ListCommands.php
Normal file
197
system/Commands/ListCommands.php
Normal file
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* CI Help command for the spark script.
|
||||
*
|
||||
* Lists the basic usage information for the spark script,
|
||||
* and provides a way to list help for other commands.
|
||||
*
|
||||
* @package CodeIgniter\Commands
|
||||
*/
|
||||
class ListCommands extends BaseCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'list';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Lists the available commands.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'list';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* The length of the longest command name.
|
||||
* Used during display in columns.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $maxFirstLength = 0;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Displays the help for the spark cli script itself.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$commands = $this->commands->getCommands();
|
||||
|
||||
$this->describeCommands($commands);
|
||||
|
||||
CLI::newLine();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Displays the commands on the CLI.
|
||||
*
|
||||
* @param array $commands
|
||||
*/
|
||||
protected function describeCommands(array $commands = [])
|
||||
{
|
||||
ksort($commands);
|
||||
|
||||
// Sort into buckets by group
|
||||
$sorted = [];
|
||||
$maxTitleLength = 0;
|
||||
|
||||
foreach ($commands as $title => $command)
|
||||
{
|
||||
if (! isset($sorted[$command['group']]))
|
||||
{
|
||||
$sorted[$command['group']] = [];
|
||||
}
|
||||
|
||||
$sorted[$command['group']][$title] = $command;
|
||||
|
||||
$maxTitleLength = max($maxTitleLength, strlen($title));
|
||||
}
|
||||
|
||||
ksort($sorted);
|
||||
|
||||
// Display it all...
|
||||
foreach ($sorted as $group => $items)
|
||||
{
|
||||
CLI::newLine();
|
||||
CLI::write($group);
|
||||
|
||||
foreach ($items as $title => $item)
|
||||
{
|
||||
$title = $this->padTitle($title, $maxTitleLength, 2, 2);
|
||||
|
||||
$out = CLI::color($title, 'yellow');
|
||||
|
||||
if (isset($item['description']))
|
||||
{
|
||||
$out .= CLI::wrap($item['description'], 125, strlen($title));
|
||||
}
|
||||
|
||||
CLI::write($out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Pads our string out so that all titles are the same length to nicely line up descriptions.
|
||||
*
|
||||
* @param string $item
|
||||
* @param integer $max
|
||||
* @param integer $extra // How many extra spaces to add at the end
|
||||
* @param integer $indent
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function padTitle(string $item, int $max, int $extra = 2, int $indent = 0): string
|
||||
{
|
||||
$max += $extra + $indent;
|
||||
|
||||
$item = str_repeat(' ', $indent) . $item;
|
||||
$item = str_pad($item, $max);
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
145
system/Commands/Server/Serve.php
Normal file
145
system/Commands/Server/Serve.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Server;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* Launch the PHP development server
|
||||
*
|
||||
* Not testable, as it throws phpunit for a loop :-/
|
||||
*
|
||||
* @package CodeIgniter\Commands\Server
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Serve extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* Minimum PHP version
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $minPHPVersion = '7.2';
|
||||
|
||||
/**
|
||||
* Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'serve';
|
||||
|
||||
/**
|
||||
* Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Launches the CodeIgniter PHP-Development Server.';
|
||||
|
||||
/**
|
||||
* Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'serve';
|
||||
|
||||
/**
|
||||
* Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
'-php' => 'The PHP Binary [default: "PHP_BINARY"]',
|
||||
'-host' => 'The HTTP Host [default: "localhost"]',
|
||||
'-port' => 'The HTTP Host Port [default: "8080"]',
|
||||
];
|
||||
|
||||
/**
|
||||
* Run the server
|
||||
*
|
||||
* @param array $params Parameters
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
// Valid PHP Version?
|
||||
if (phpversion() < $this->minPHPVersion)
|
||||
{
|
||||
die('Your PHP version must be ' . $this->minPHPVersion .
|
||||
' or higher to run CodeIgniter. Current version: ' . phpversion());
|
||||
}
|
||||
|
||||
// Collect any user-supplied options and apply them.
|
||||
$php = CLI::getOption('php') ?? PHP_BINARY;
|
||||
$host = CLI::getOption('host') ?? 'localhost';
|
||||
$port = CLI::getOption('port') ?? '8080';
|
||||
|
||||
// Get the party started.
|
||||
CLI::write('CodeIgniter development server started on http://' . $host . ':' . $port, 'green');
|
||||
CLI::write('Press Control-C to stop.');
|
||||
|
||||
// Set the Front Controller path as Document Root.
|
||||
$docroot = escapeshellarg(FCPATH);
|
||||
|
||||
// Mimic Apache's mod_rewrite functionality with user settings.
|
||||
$rewrite = escapeshellarg(__DIR__ . '/rewrite.php');
|
||||
|
||||
// Call PHP's built-in webserver, making sure to set our
|
||||
// base path to the public folder, and to use the rewrite file
|
||||
// to ensure our environment is set and it simulates basic mod_rewrite.
|
||||
passthru($php . ' -S ' . $host . ':' . $port . ' -t ' . $docroot . ' ' . $rewrite);
|
||||
}
|
||||
|
||||
}
|
||||
35
system/Commands/Server/rewrite.php
Normal file
35
system/Commands/Server/rewrite.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter PHP-Development Server Rewrite Rules
|
||||
*
|
||||
* This script works with the CLI serve command to help run a seamless
|
||||
* development server based around PHP's built-in development
|
||||
* server. This file simply tries to mimic Apache's mod_rewrite
|
||||
* functionality so the site will operate as normal.
|
||||
*/
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
// Avoid this file run when listing commands
|
||||
if (php_sapi_name() === 'cli')
|
||||
{
|
||||
return;
|
||||
}
|
||||
$uri = urldecode(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
|
||||
|
||||
// Front Controller path - expected to be in the default folder
|
||||
$fcpath = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR;
|
||||
|
||||
// Full path
|
||||
$path = $fcpath . ltrim($uri, '/');
|
||||
|
||||
// If $path is an existing file or folder within the public folder
|
||||
// then let the request handle it like normal.
|
||||
if ($uri !== '/' && (is_file($path) || is_dir($path)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, we'll load the index file and let
|
||||
// the framework handle the request from here.
|
||||
require_once $fcpath . 'index.php';
|
||||
// @codeCoverageIgnoreEnd
|
||||
136
system/Commands/Sessions/CreateMigration.php
Normal file
136
system/Commands/Sessions/CreateMigration.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Sessions;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Config\App;
|
||||
|
||||
/**
|
||||
* Creates a migration file for database sessions.
|
||||
*
|
||||
* @package CodeIgniter\Commands
|
||||
*/
|
||||
|
||||
class CreateMigration extends BaseCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'session:migration';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates the migration file for database sessions.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'session:migration';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
'-n' => 'Set migration namespace',
|
||||
'-g' => 'Set database group',
|
||||
'-t' => 'Set table name',
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates a new migration file with the current timestamp.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function run(array $params = [])
|
||||
{
|
||||
$config = new App();
|
||||
|
||||
$tableName = CLI::getOption('t') ?? 'ci_sessions';
|
||||
|
||||
$path = APPPATH . 'Database/Migrations/' . date('YmdHis_') . 'create_' . $tableName . '_table' . '.php';
|
||||
|
||||
$data = [
|
||||
'namespace' => CLI::getOption('n') ?? APP_NAMESPACE ?? 'App',
|
||||
'DBGroup' => CLI::getOption('g'),
|
||||
'tableName' => $tableName,
|
||||
'matchIP' => $config->sessionMatchIP ?? false,
|
||||
];
|
||||
|
||||
$template = view('\CodeIgniter\Commands\Sessions\Views\migration.tpl.php', $data, ['debug' => false]);
|
||||
$template = str_replace('@php', '<?php', $template);
|
||||
|
||||
// Write the file out.
|
||||
helper('filesystem');
|
||||
if (! write_file($path, $template))
|
||||
{
|
||||
CLI::error(lang('Migrations.migWriteError'));
|
||||
return;
|
||||
}
|
||||
|
||||
CLI::write('Created file: ' . CLI::color(str_replace(APPPATH, 'APPPATH/', $path), 'green'));
|
||||
}
|
||||
|
||||
}
|
||||
52
system/Commands/Sessions/Views/migration.tpl.php
Normal file
52
system/Commands/Sessions/Views/migration.tpl.php
Normal file
@@ -0,0 +1,52 @@
|
||||
@php namespace <?= $namespace ?>\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class Migration_create_<?= $tableName ?>_table extends Migration
|
||||
{
|
||||
<?php if (isset($DBGroup)) : ?>
|
||||
protected $DBGroup = '<?= $DBGroup ?>';
|
||||
<?php endif ?>
|
||||
|
||||
public function up()
|
||||
{
|
||||
$this->forge->addField([
|
||||
'id' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 128,
|
||||
'null' => false
|
||||
],
|
||||
'ip_address' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 45,
|
||||
'null' => false
|
||||
],
|
||||
'timestamp' => [
|
||||
'type' => 'INT',
|
||||
'constraint' => 10,
|
||||
'unsigned' => true,
|
||||
'null' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'data' => [
|
||||
'type' => 'TEXT',
|
||||
'null' => false,
|
||||
'default' => ''
|
||||
],
|
||||
]);
|
||||
<?php if ($matchIP === true) : ?>
|
||||
$this->forge->addKey(['id', 'ip_address'], true);
|
||||
<?php else: ?>
|
||||
$this->forge->addKey('id', true);
|
||||
<?php endif ?>
|
||||
$this->forge->addKey('timestamp');
|
||||
$this->forge->createTable('<?= $tableName ?>', true);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->forge->dropTable('<?= $tableName ?>', true);
|
||||
}
|
||||
}
|
||||
131
system/Commands/Utilities/Namespaces.php
Normal file
131
system/Commands/Utilities/Namespaces.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Utilities;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Config\Autoload;
|
||||
|
||||
/**
|
||||
* Lists namespaces set in Config\Autoload with their
|
||||
* full server path. Helps you to verify that you have
|
||||
* the namespaces setup correctly.
|
||||
*
|
||||
* @package CodeIgniter\Commands
|
||||
*/
|
||||
class Namespaces extends BaseCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'namespaces';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Verifies your namespaces are setup correctly.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'namespaces';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Displays the help for the spark cli script itself.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$config = new Autoload();
|
||||
|
||||
$tbody = [];
|
||||
foreach ($config->psr4 as $ns => $path)
|
||||
{
|
||||
$path = realpath($path) ?? $path;
|
||||
|
||||
$tbody[] = [
|
||||
$ns,
|
||||
realpath($path) ?? $path,
|
||||
is_dir($path) ? 'Yes' : 'MISSING',
|
||||
];
|
||||
}
|
||||
|
||||
$thead = [
|
||||
'Namespace',
|
||||
'Path',
|
||||
'Found?',
|
||||
];
|
||||
|
||||
CLI::table($tbody, $thead);
|
||||
}
|
||||
|
||||
}
|
||||
150
system/Commands/Utilities/Routes.php
Normal file
150
system/Commands/Utilities/Routes.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Utilities;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Config\Services;
|
||||
|
||||
/**
|
||||
* Lists all of the user-defined routes. This will include any Routes files
|
||||
* that can be discovered, but will NOT include any routes that are not defined
|
||||
* in a routes file, but are instead discovered through auto-routing.
|
||||
*
|
||||
* @package CodeIgniter\Commands
|
||||
*/
|
||||
class Routes extends BaseCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'routes';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Displays all of user-defined routes. Does NOT display auto-detected routes.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'routes';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Displays the help for the spark cli script itself.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$collection = Services::routes(true);
|
||||
$methods = [
|
||||
'get',
|
||||
'head',
|
||||
'post',
|
||||
'patch',
|
||||
'put',
|
||||
'delete',
|
||||
'options',
|
||||
'trace',
|
||||
'connect',
|
||||
'cli',
|
||||
];
|
||||
|
||||
$tbody = [];
|
||||
foreach ($methods as $method)
|
||||
{
|
||||
$routes = $collection->getRoutes($method);
|
||||
|
||||
foreach ($routes as $from => $to)
|
||||
{
|
||||
// filter for strings, as callbacks aren't displayable
|
||||
if (is_string($to))
|
||||
{
|
||||
$tbody[] = [
|
||||
$from,
|
||||
$method,
|
||||
$to,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$thead = [
|
||||
'Route',
|
||||
'Method',
|
||||
'Command',
|
||||
];
|
||||
|
||||
CLI::table($tbody, $thead);
|
||||
}
|
||||
|
||||
}
|
||||
1137
system/Common.php
Normal file
1137
system/Common.php
Normal file
File diff suppressed because it is too large
Load Diff
221
system/ComposerScripts.php
Normal file
221
system/ComposerScripts.php
Normal file
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter;
|
||||
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* ComposerScripts
|
||||
*
|
||||
* These scripts are used by Composer during installs and updates
|
||||
* to move files to locations within the system folder so that end-users
|
||||
* do not need to use Composer to install a package, but can simply
|
||||
* download
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @package CodeIgniter
|
||||
*/
|
||||
class ComposerScripts
|
||||
{
|
||||
/**
|
||||
* Base path to use.
|
||||
*
|
||||
* @var type
|
||||
*/
|
||||
protected static $basePath = 'ThirdParty/';
|
||||
|
||||
/**
|
||||
* After composer install/update, this is called to move
|
||||
* the bare-minimum required files for our dependencies
|
||||
* to appropriate locations.
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public static function postUpdate()
|
||||
{
|
||||
static::moveEscaper();
|
||||
static::moveKint();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Move a file.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $destination
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected static function moveFile(string $source, string $destination): bool
|
||||
{
|
||||
$source = realpath($source);
|
||||
|
||||
if (empty($source))
|
||||
{
|
||||
die('Cannot move file. Source path invalid.');
|
||||
}
|
||||
|
||||
if (! is_file($source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return copy($source, $destination);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determine file path of a class.
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return string
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected static function getClassFilePath(string $class)
|
||||
{
|
||||
$reflector = new ReflectionClass($class);
|
||||
|
||||
return $reflector->getFileName();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A recursive remove directory method.
|
||||
*
|
||||
* @param $dir
|
||||
*/
|
||||
protected static function removeDir($dir)
|
||||
{
|
||||
if (is_dir($dir))
|
||||
{
|
||||
$objects = scandir($dir);
|
||||
foreach ($objects as $object)
|
||||
{
|
||||
if ($object !== '.' && $object !== '..')
|
||||
{
|
||||
if (filetype($dir . '/' . $object) === 'dir')
|
||||
{
|
||||
static::removeDir($dir . '/' . $object);
|
||||
}
|
||||
else
|
||||
{
|
||||
unlink($dir . '/' . $object);
|
||||
}
|
||||
}
|
||||
}
|
||||
reset($objects);
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the Zend Escaper files into our base repo so that it's
|
||||
* available for packaged releases where the users don't user Composer.
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public static function moveEscaper()
|
||||
{
|
||||
if (class_exists('\\Zend\\Escaper\\Escaper') && is_file(static::getClassFilePath('\\Zend\\Escaper\\Escaper')))
|
||||
{
|
||||
$base = basename(__DIR__) . '/' . static::$basePath . 'ZendEscaper';
|
||||
|
||||
foreach ([$base, $base . '/Exception'] as $path)
|
||||
{
|
||||
if (! is_dir($path))
|
||||
{
|
||||
mkdir($path, 0755);
|
||||
}
|
||||
}
|
||||
|
||||
$files = [
|
||||
static::getClassFilePath('\\Zend\\Escaper\\Exception\\ExceptionInterface') => $base . '/Exception/ExceptionInterface.php',
|
||||
static::getClassFilePath('\\Zend\\Escaper\\Exception\\InvalidArgumentException') => $base . '/Exception/InvalidArgumentException.php',
|
||||
static::getClassFilePath('\\Zend\\Escaper\\Exception\\RuntimeException') => $base . '/Exception/RuntimeException.php',
|
||||
static::getClassFilePath('\\Zend\\Escaper\\Escaper') => $base . '/Escaper.php',
|
||||
];
|
||||
|
||||
foreach ($files as $source => $dest)
|
||||
{
|
||||
if (! static::moveFile($source, $dest))
|
||||
{
|
||||
die('Error moving: ' . $source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Moves the Kint file into our base repo so that it's
|
||||
* available for packaged releases where the users don't user Composer.
|
||||
*/
|
||||
public static function moveKint()
|
||||
{
|
||||
$filename = 'vendor/kint-php/kint/build/kint-aante-light.php';
|
||||
|
||||
if (is_file($filename))
|
||||
{
|
||||
$base = basename(__DIR__) . '/' . static::$basePath . 'Kint';
|
||||
|
||||
// Remove the contents of the previous Kint folder, if any.
|
||||
if (is_dir($base))
|
||||
{
|
||||
static::removeDir($base);
|
||||
}
|
||||
|
||||
// Create Kint if it doesn't exist already
|
||||
if (! is_dir($base))
|
||||
{
|
||||
mkdir($base, 0755);
|
||||
}
|
||||
|
||||
if (! static::moveFile($filename, $base . '/kint.php'))
|
||||
{
|
||||
die('Error moving: ' . $filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
138
system/Config/AutoloadConfig.php
Normal file
138
system/Config/AutoloadConfig.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Config;
|
||||
|
||||
/**
|
||||
* AUTO-LOADER
|
||||
*
|
||||
* This file defines the namespaces and class maps so the Autoloader
|
||||
* can find the files as needed.
|
||||
*/
|
||||
class AutoloadConfig
|
||||
{
|
||||
|
||||
/**
|
||||
* Array of namespaces for autoloading.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $psr4 = [];
|
||||
|
||||
/**
|
||||
* Map of class names and locations
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $classmap = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
/**
|
||||
* -------------------------------------------------------------------
|
||||
* Namespaces
|
||||
* -------------------------------------------------------------------
|
||||
* This maps the locations of any namespaces in your application
|
||||
* to their location on the file system. These are used by the
|
||||
* Autoloader to locate files the first time they have been instantiated.
|
||||
*
|
||||
* The '/application' and '/system' directories are already mapped for
|
||||
* you. You may change the name of the 'App' namespace if you wish,
|
||||
* but this should be done prior to creating any namespaced classes,
|
||||
* else you will need to modify all of those classes for this to work.
|
||||
*
|
||||
* DO NOT change the name of the CodeIgniter namespace or your application
|
||||
* WILL break. *
|
||||
* Prototype:
|
||||
*
|
||||
* $Config['psr4'] = [
|
||||
* 'CodeIgniter' => SYSPATH
|
||||
* `];
|
||||
*/
|
||||
$this->psr4 = [
|
||||
'CodeIgniter' => realpath(SYSTEMPATH),
|
||||
];
|
||||
|
||||
if (isset($_SERVER['CI_ENVIRONMENT']) && $_SERVER['CI_ENVIRONMENT'] === 'testing')
|
||||
{
|
||||
$this->psr4['Tests\Support'] = SUPPORTPATH;
|
||||
}
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------
|
||||
* Class Map
|
||||
* -------------------------------------------------------------------
|
||||
* The class map provides a map of class names and their exact
|
||||
* location on the drive. Classes loaded in this manner will have
|
||||
* slightly faster performance because they will not have to be
|
||||
* searched for within one or more directories as they would if they
|
||||
* were being autoloaded through a namespace.
|
||||
*
|
||||
* Prototype:
|
||||
*
|
||||
* $Config['classmap'] = [
|
||||
* 'MyClass' => '/path/to/class/file.php'
|
||||
* ];
|
||||
*/
|
||||
$this->classmap = [
|
||||
'Psr\Log\AbstractLogger' => SYSTEMPATH . 'ThirdParty/PSR/Log/AbstractLogger.php',
|
||||
'Psr\Log\InvalidArgumentException' => SYSTEMPATH . 'ThirdParty/PSR/Log/InvalidArgumentException.php',
|
||||
'Psr\Log\LoggerAwareInterface' => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerAwareInterface.php',
|
||||
'Psr\Log\LoggerAwareTrait' => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerAwareTrait.php',
|
||||
'Psr\Log\LoggerInterface' => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerInterface.php',
|
||||
'Psr\Log\LoggerTrait' => SYSTEMPATH . 'ThirdParty/PSR/Log/LoggerTrait.php',
|
||||
'Psr\Log\LogLevel' => SYSTEMPATH . 'ThirdParty/PSR/Log/LogLevel.php',
|
||||
'Psr\Log\NullLogger' => SYSTEMPATH . 'ThirdParty/PSR/Log/NullLogger.php',
|
||||
'Zend\Escaper\Escaper' => SYSTEMPATH . 'ThirdParty/ZendEscaper/Escaper.php',
|
||||
];
|
||||
|
||||
if (isset($_SERVER['CI_ENVIRONMENT']) && $_SERVER['CI_ENVIRONMENT'] === 'testing')
|
||||
{
|
||||
$this->classmap['CodeIgniter\Log\TestLogger'] = SUPPORTPATH . 'Log/TestLogger.php';
|
||||
$this->classmap['CIDatabaseTestCase'] = SUPPORTPATH . 'CIDatabaseTestCase.php';
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
245
system/Config/BaseConfig.php
Normal file
245
system/Config/BaseConfig.php
Normal file
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Config;
|
||||
|
||||
/**
|
||||
* Class BaseConfig
|
||||
*
|
||||
* Not intended to be used on its own, this class will attempt to
|
||||
* automatically populate the child class' properties with values
|
||||
* from the environment.
|
||||
*
|
||||
* These can be set within the .env file.
|
||||
*/
|
||||
class BaseConfig
|
||||
{
|
||||
|
||||
/**
|
||||
* An optional array of classes that will act as Registrars
|
||||
* for rapidly setting config class properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $registrars = [];
|
||||
|
||||
/**
|
||||
* Has module discovery happened yet?
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected static $didDiscovery = false;
|
||||
|
||||
/**
|
||||
* The modules configuration.
|
||||
*
|
||||
* @var type
|
||||
*/
|
||||
protected static $moduleConfig;
|
||||
|
||||
/**
|
||||
* Will attempt to get environment variables with names
|
||||
* that match the properties of the child class.
|
||||
*
|
||||
* The "shortPrefix" is the lowercase-only config class name.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
static::$moduleConfig = config('Modules');
|
||||
|
||||
$properties = array_keys(get_object_vars($this));
|
||||
$prefix = get_class($this);
|
||||
$slashAt = strrpos($prefix, '\\');
|
||||
$shortPrefix = strtolower(substr($prefix, $slashAt === false ? 0 : $slashAt + 1));
|
||||
|
||||
foreach ($properties as $property)
|
||||
{
|
||||
$this->initEnvValue($this->$property, $property, $prefix, $shortPrefix);
|
||||
}
|
||||
|
||||
if (defined('ENVIRONMENT') && ENVIRONMENT !== 'testing')
|
||||
{
|
||||
// well, this won't happen during unit testing
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->registerProperties();
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Initialization an environment-specific configuration setting
|
||||
*
|
||||
* @param mixed &$property
|
||||
* @param string $name
|
||||
* @param string $prefix
|
||||
* @param string $shortPrefix
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function initEnvValue(&$property, string $name, string $prefix, string $shortPrefix)
|
||||
{
|
||||
if (is_array($property))
|
||||
{
|
||||
foreach ($property as $key => $val)
|
||||
{
|
||||
$this->initEnvValue($property[$key], "{$name}.{$key}", $prefix, $shortPrefix);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (($value = $this->getEnvValue($name, $prefix, $shortPrefix)) !== false)
|
||||
{
|
||||
if (! is_null($value))
|
||||
{
|
||||
if ($value === 'false')
|
||||
{
|
||||
$value = false;
|
||||
}
|
||||
elseif ($value === 'true')
|
||||
{
|
||||
$value = true;
|
||||
}
|
||||
|
||||
$property = is_bool($value) ? $value : trim($value, '\'"');
|
||||
}
|
||||
}
|
||||
}
|
||||
return $property;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Retrieve an environment-specific configuration setting
|
||||
*
|
||||
* @param string $property
|
||||
* @param string $prefix
|
||||
* @param string $shortPrefix
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getEnvValue(string $property, string $prefix, string $shortPrefix)
|
||||
{
|
||||
$shortPrefix = ltrim($shortPrefix, '\\');
|
||||
switch (true)
|
||||
{
|
||||
case array_key_exists("{$shortPrefix}.{$property}", $_ENV):
|
||||
return $_ENV["{$shortPrefix}.{$property}"];
|
||||
break;
|
||||
case array_key_exists("{$shortPrefix}.{$property}", $_SERVER):
|
||||
return $_SERVER["{$shortPrefix}.{$property}"];
|
||||
break;
|
||||
case array_key_exists("{$prefix}.{$property}", $_ENV):
|
||||
return $_ENV["{$prefix}.{$property}"];
|
||||
break;
|
||||
case array_key_exists("{$prefix}.{$property}", $_SERVER):
|
||||
return $_SERVER["{$prefix}.{$property}"];
|
||||
break;
|
||||
default:
|
||||
$value = getenv($property);
|
||||
return $value === false ? null : $value;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Provides external libraries a simple way to register one or more
|
||||
* options into a config file.
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function registerProperties()
|
||||
{
|
||||
if (! static::$moduleConfig->shouldDiscover('registrars'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (! static::$didDiscovery)
|
||||
{
|
||||
$locator = \Config\Services::locator();
|
||||
$registrarsFiles = $locator->search('Config/Registrar.php');
|
||||
|
||||
foreach ($registrarsFiles as $file)
|
||||
{
|
||||
$className = $locator->getClassname($file);
|
||||
static::$registrars[] = new $className();
|
||||
}
|
||||
|
||||
static::$didDiscovery = true;
|
||||
}
|
||||
|
||||
$shortName = (new \ReflectionClass($this))->getShortName();
|
||||
|
||||
// Check the registrar class for a method named after this class' shortName
|
||||
foreach (static::$registrars as $callable)
|
||||
{
|
||||
// ignore non-applicable registrars
|
||||
if (! method_exists($callable, $shortName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$properties = $callable::$shortName();
|
||||
|
||||
if (! is_array($properties))
|
||||
{
|
||||
throw new \RuntimeException('Registrars must return an array of properties and their values.');
|
||||
}
|
||||
|
||||
foreach ($properties as $property => $value)
|
||||
{
|
||||
if (isset($this->$property) && is_array($this->$property) && is_array($value))
|
||||
{
|
||||
$this->$property = array_merge($this->$property, $value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->$property = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
297
system/Config/BaseService.php
Normal file
297
system/Config/BaseService.php
Normal file
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Config;
|
||||
|
||||
use CodeIgniter\Autoloader\Autoloader;
|
||||
use CodeIgniter\Autoloader\FileLocator;
|
||||
use Config\Autoload;
|
||||
use Config\Modules;
|
||||
|
||||
/**
|
||||
* Services Configuration file.
|
||||
*
|
||||
* Services are simply other classes/libraries that the system uses
|
||||
* to do its job. This is used by CodeIgniter to allow the core of the
|
||||
* framework to be swapped out easily without affecting the usage within
|
||||
* the rest of your application.
|
||||
*
|
||||
* This is used in place of a Dependency Injection container primarily
|
||||
* due to its simplicity, which allows a better long-term maintenance
|
||||
* of the applications built on top of CodeIgniter. A bonus side-effect
|
||||
* is that IDEs are able to determine what class you are calling
|
||||
* whereas with DI Containers there usually isn't a way for them to do this.
|
||||
*
|
||||
* @see http://blog.ircmaxell.com/2015/11/simple-easy-risk-and-change.html
|
||||
* @see http://www.infoq.com/presentations/Simple-Made-Easy
|
||||
*/
|
||||
class BaseService
|
||||
{
|
||||
|
||||
/**
|
||||
* Cache for instance of any services that
|
||||
* have been requested as a "shared" instance.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static protected $instances = [];
|
||||
|
||||
/**
|
||||
* Mock objects for testing which are returned if exist.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static protected $mocks = [];
|
||||
|
||||
/**
|
||||
* Have we already discovered other Services?
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
static protected $discovered = false;
|
||||
|
||||
/**
|
||||
* A cache of other service classes we've found.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static protected $services = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a shared instance of any of the class' services.
|
||||
*
|
||||
* $key must be a name matching a service.
|
||||
*
|
||||
* @param string $key
|
||||
* @param array ...$params
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected static function getSharedInstance(string $key, ...$params)
|
||||
{
|
||||
// Returns mock if exists
|
||||
if (isset(static::$mocks[$key]))
|
||||
{
|
||||
return static::$mocks[$key];
|
||||
}
|
||||
|
||||
if (! isset(static::$instances[$key]))
|
||||
{
|
||||
// Make sure $getShared is false
|
||||
array_push($params, false);
|
||||
|
||||
static::$instances[$key] = static::$key(...$params);
|
||||
}
|
||||
|
||||
return static::$instances[$key];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Autoloader class is the central class that handles our
|
||||
* spl_autoload_register method, and helper methods.
|
||||
*
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Autoloader\Autoloader
|
||||
*/
|
||||
public static function autoloader(bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
if (empty(static::$instances['autoloader']))
|
||||
{
|
||||
static::$instances['autoloader'] = new Autoloader();
|
||||
}
|
||||
|
||||
return static::$instances['autoloader'];
|
||||
}
|
||||
|
||||
return new Autoloader();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The file locator provides utility methods for looking for non-classes
|
||||
* within namespaced folders, as well as convenience methods for
|
||||
* loading 'helpers', and 'libraries'.
|
||||
*
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Autoloader\FileLocator
|
||||
*/
|
||||
public static function locator(bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
if (empty(static::$instances['locator']))
|
||||
{
|
||||
static::$instances['locator'] = new FileLocator(
|
||||
static::autoloader()
|
||||
);
|
||||
}
|
||||
|
||||
return static::$instances['locator'];
|
||||
}
|
||||
|
||||
return new FileLocator(static::autoloader());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Provides the ability to perform case-insensitive calling of service
|
||||
* names.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function __callStatic(string $name, array $arguments)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
|
||||
if (method_exists(Services::class, $name))
|
||||
{
|
||||
return Services::$name(...$arguments);
|
||||
}
|
||||
|
||||
return static::discoverServices($name, $arguments);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Reset shared instances and mocks for testing.
|
||||
*
|
||||
* @param boolean $init_autoloader Initializes autoloader instance
|
||||
*/
|
||||
public static function reset(bool $init_autoloader = false)
|
||||
{
|
||||
static::$mocks = [];
|
||||
|
||||
static::$instances = [];
|
||||
|
||||
if ($init_autoloader)
|
||||
{
|
||||
static::autoloader()->initialize(new Autoload(), new Modules());
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Inject mock object for testing.
|
||||
*
|
||||
* @param string $name
|
||||
* @param $mock
|
||||
*/
|
||||
public static function injectMock(string $name, $mock)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
static::$mocks[$name] = $mock;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will scan all psr4 namespaces registered with system to look
|
||||
* for new Config\Services files. Caches a copy of each one, then
|
||||
* looks for the service method in each, returning an instance of
|
||||
* the service, if available.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected static function discoverServices(string $name, array $arguments)
|
||||
{
|
||||
if (! static::$discovered)
|
||||
{
|
||||
$config = config('Modules');
|
||||
|
||||
if ($config->shouldDiscover('services'))
|
||||
{
|
||||
$locator = static::locator();
|
||||
$files = $locator->search('Config/Services');
|
||||
|
||||
if (empty($files))
|
||||
{
|
||||
// no files at all found - this would be really, really bad
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get instances of all service classes and cache them locally.
|
||||
foreach ($files as $file)
|
||||
{
|
||||
$classname = $locator->getClassname($file);
|
||||
|
||||
if (! in_array($classname, ['CodeIgniter\\Config\\Services']))
|
||||
{
|
||||
static::$services[] = new $classname();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static::$discovered = true;
|
||||
}
|
||||
|
||||
if (! static::$services)
|
||||
{
|
||||
// we found stuff, but no services - this would be really bad
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to find the desired service method
|
||||
foreach (static::$services as $class)
|
||||
{
|
||||
if (method_exists(get_class($class), $name))
|
||||
{
|
||||
return $class::$name(...$arguments);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
161
system/Config/Config.php
Normal file
161
system/Config/Config.php
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Config;
|
||||
|
||||
/**
|
||||
* Class Config
|
||||
*
|
||||
* @package CodeIgniter\Config
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
/**
|
||||
* Cache for instance of any configurations that
|
||||
* have been requested as "shared" instance.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static private $instances = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create new configuration instances or return
|
||||
* a shared instance
|
||||
*
|
||||
* @param string $name Configuration name
|
||||
* @param boolean $getShared Use shared instance
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function get(string $name, bool $getShared = true)
|
||||
{
|
||||
$class = $name;
|
||||
if (($pos = strrpos($name, '\\')) !== false)
|
||||
{
|
||||
$class = substr($name, $pos + 1);
|
||||
}
|
||||
|
||||
if (! $getShared)
|
||||
{
|
||||
return self::createClass($name);
|
||||
}
|
||||
|
||||
if (! isset( self::$instances[$class] ))
|
||||
{
|
||||
self::$instances[$class] = self::createClass($name);
|
||||
}
|
||||
return self::$instances[$class];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Helper method for injecting mock instances while testing.
|
||||
*
|
||||
* @param string $class
|
||||
* @param $instance
|
||||
*/
|
||||
public static function injectMock(string $class, $instance)
|
||||
{
|
||||
self::$instances[$class] = $instance;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Resets the instances array
|
||||
*/
|
||||
public static function reset()
|
||||
{
|
||||
static::$instances = [];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Find configuration class and create instance
|
||||
*
|
||||
* @param string $name Classname
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
private static function createClass(string $name)
|
||||
{
|
||||
if (class_exists($name))
|
||||
{
|
||||
return new $name();
|
||||
}
|
||||
|
||||
$locator = Services::locator();
|
||||
$file = $locator->locateFile($name, 'Config');
|
||||
|
||||
if (empty($file))
|
||||
{
|
||||
// No file found - check if the class was namespaced
|
||||
if (strpos($name, '\\') !== false)
|
||||
{
|
||||
// Class was namespaced and locateFile couldn't find it
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check all namespaces
|
||||
$files = $locator->search('Config/' . $name);
|
||||
if (empty($files))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the first match (prioritizes user and framework)
|
||||
$file = reset($files);
|
||||
}
|
||||
|
||||
$name = $locator->getClassname($file);
|
||||
|
||||
if (empty($name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new $name();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
307
system/Config/DotEnv.php
Normal file
307
system/Config/DotEnv.php
Normal file
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Config;
|
||||
|
||||
/**
|
||||
* Environment-specific configuration
|
||||
*/
|
||||
class DotEnv
|
||||
{
|
||||
|
||||
/**
|
||||
* The directory where the .env file can be located.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Builds the path to our file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $file
|
||||
*/
|
||||
public function __construct(string $path, string $file = '.env')
|
||||
{
|
||||
$this->path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The main entry point, will load the .env file and process it
|
||||
* so that we end up with all settings in the PHP environment vars
|
||||
* (i.e. getenv(), $_ENV, and $_SERVER)
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function load(): bool
|
||||
{
|
||||
// We don't want to enforce the presence of a .env file,
|
||||
// they should be optional.
|
||||
if (! is_file($this->path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure file is readable
|
||||
if (! is_readable($this->path))
|
||||
{
|
||||
throw new \InvalidArgumentException("The .env file is not readable: {$this->path}");
|
||||
}
|
||||
|
||||
$lines = file($this->path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
|
||||
foreach ($lines as $line)
|
||||
{
|
||||
// Is it a comment?
|
||||
if (strpos(trim($line), '#') === 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there is an equal sign, then we know we
|
||||
// are assigning a variable.
|
||||
if (strpos($line, '=') !== false)
|
||||
{
|
||||
$this->setVariable($line);
|
||||
}
|
||||
}
|
||||
|
||||
return true; // for success
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sets the variable into the environment. Will parse the string
|
||||
* first to look for {name}={value} pattern, ensure that nested
|
||||
* variables are handled, and strip it of single and double quotes.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
*/
|
||||
protected function setVariable(string $name, string $value = '')
|
||||
{
|
||||
list($name, $value) = $this->normaliseVariable($name, $value);
|
||||
|
||||
if (! getenv($name, true))
|
||||
{
|
||||
putenv("$name=$value");
|
||||
}
|
||||
if (empty($_ENV[$name]))
|
||||
{
|
||||
$_ENV[$name] = $value;
|
||||
}
|
||||
if (empty($_SERVER[$name]))
|
||||
{
|
||||
$_SERVER[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parses for assignment, cleans the $name and $value, and ensures
|
||||
* that nested variables are handled.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function normaliseVariable(string $name, string $value = ''): array
|
||||
{
|
||||
// Split our compound string into it's parts.
|
||||
if (strpos($name, '=') !== false)
|
||||
{
|
||||
list($name, $value) = explode('=', $name, 2);
|
||||
}
|
||||
|
||||
$name = trim($name);
|
||||
$value = trim($value);
|
||||
|
||||
// Sanitize the name
|
||||
$name = str_replace(['export', '\'', '"'], '', $name);
|
||||
|
||||
// Sanitize the value
|
||||
$value = $this->sanitizeValue($value);
|
||||
|
||||
$value = $this->resolveNestedVariables($value);
|
||||
|
||||
return [
|
||||
$name,
|
||||
$value,
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Strips quotes from the environment variable value.
|
||||
*
|
||||
* This was borrowed from the excellent phpdotenv with very few changes.
|
||||
* https://github.com/vlucas/phpdotenv
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function sanitizeValue(string $value): string
|
||||
{
|
||||
if (! $value)
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Does it begin with a quote?
|
||||
if (strpbrk($value[0], '"\'') !== false)
|
||||
{
|
||||
// value starts with a quote
|
||||
$quote = $value[0];
|
||||
$regexPattern = sprintf(
|
||||
'/^
|
||||
%1$s # match a quote at the start of the value
|
||||
( # capturing sub-pattern used
|
||||
(?: # we do not need to capture this
|
||||
[^%1$s\\\\] # any character other than a quote or backslash
|
||||
|\\\\\\\\ # or two backslashes together
|
||||
|\\\\%1$s # or an escaped quote e.g \"
|
||||
)* # as many characters that match the previous rules
|
||||
) # end of the capturing sub-pattern
|
||||
%1$s # and the closing quote
|
||||
.*$ # and discard any string after the closing quote
|
||||
/mx', $quote
|
||||
);
|
||||
$value = preg_replace($regexPattern, '$1', $value);
|
||||
$value = str_replace("\\$quote", $quote, $value);
|
||||
$value = str_replace('\\\\', '\\', $value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$parts = explode(' #', $value, 2);
|
||||
|
||||
$value = trim($parts[0]);
|
||||
|
||||
// Unquoted values cannot contain whitespace
|
||||
if (preg_match('/\s+/', $value) > 0)
|
||||
{
|
||||
throw new \InvalidArgumentException('.env values containing spaces must be surrounded by quotes.');
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Resolve the nested variables.
|
||||
*
|
||||
* Look for ${varname} patterns in the variable value and replace with an existing
|
||||
* environment variable.
|
||||
*
|
||||
* This was borrowed from the excellent phpdotenv with very few changes.
|
||||
* https://github.com/vlucas/phpdotenv
|
||||
*
|
||||
* @param $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveNestedVariables(string $value): string
|
||||
{
|
||||
if (strpos($value, '$') !== false)
|
||||
{
|
||||
$loader = $this;
|
||||
|
||||
$value = preg_replace_callback(
|
||||
'/\${([a-zA-Z0-9_]+)}/',
|
||||
function ($matchedPatterns) use ($loader) {
|
||||
$nestedVariable = $loader->getVariable($matchedPatterns[1]);
|
||||
|
||||
if (is_null($nestedVariable))
|
||||
{
|
||||
return $matchedPatterns[0];
|
||||
}
|
||||
|
||||
return $nestedVariable;
|
||||
},
|
||||
$value
|
||||
);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Search the different places for environment variables and return first value found.
|
||||
*
|
||||
* This was borrowed from the excellent phpdotenv with very few changes.
|
||||
* https://github.com/vlucas/phpdotenv
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getVariable(string $name)
|
||||
{
|
||||
switch (true)
|
||||
{
|
||||
case array_key_exists($name, $_ENV):
|
||||
return $_ENV[$name];
|
||||
break;
|
||||
case array_key_exists($name, $_SERVER):
|
||||
return $_SERVER[$name];
|
||||
break;
|
||||
default:
|
||||
$value = getenv($name);
|
||||
|
||||
// switch getenv default to null
|
||||
return $value === false ? null : $value;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
143
system/Config/ForeignCharacters.php
Normal file
143
system/Config/ForeignCharacters.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Config;
|
||||
|
||||
/**
|
||||
* Describes foreign characters for transliteration with the text helper.
|
||||
*/
|
||||
class ForeignCharacters
|
||||
{
|
||||
/**
|
||||
* Without further ado, the list of foreign characters.
|
||||
*/
|
||||
|
||||
public $characterList = [
|
||||
'/ä|æ|ǽ/' => 'ae',
|
||||
'/ö|œ/' => 'oe',
|
||||
'/ü/' => 'ue',
|
||||
'/Ä/' => 'Ae',
|
||||
'/Ü/' => 'Ue',
|
||||
'/Ö/' => 'Oe',
|
||||
'/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ|Α|Ά|Ả|Ạ|Ầ|Ẫ|Ẩ|Ậ|Ằ|Ắ|Ẵ|Ẳ|Ặ|А/' => 'A',
|
||||
'/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª|α|ά|ả|ạ|ầ|ấ|ẫ|ẩ|ậ|ằ|ắ|ẵ|ẳ|ặ|а/' => 'a',
|
||||
'/Б/' => 'B',
|
||||
'/б/' => 'b',
|
||||
'/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
|
||||
'/ç|ć|ĉ|ċ|č/' => 'c',
|
||||
'/Д/' => 'D',
|
||||
'/д/' => 'd',
|
||||
'/Ð|Ď|Đ|Δ/' => 'Dj',
|
||||
'/ð|ď|đ|δ/' => 'dj',
|
||||
'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě|Ε|Έ|Ẽ|Ẻ|Ẹ|Ề|Ế|Ễ|Ể|Ệ|Е|Э/' => 'E',
|
||||
'/è|é|ê|ë|ē|ĕ|ė|ę|ě|έ|ε|ẽ|ẻ|ẹ|ề|ế|ễ|ể|ệ|е|э/' => 'e',
|
||||
'/Ф/' => 'F',
|
||||
'/ф/' => 'f',
|
||||
'/Ĝ|Ğ|Ġ|Ģ|Γ|Г|Ґ/' => 'G',
|
||||
'/ĝ|ğ|ġ|ģ|γ|г|ґ/' => 'g',
|
||||
'/Ĥ|Ħ/' => 'H',
|
||||
'/ĥ|ħ/' => 'h',
|
||||
'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|Η|Ή|Ί|Ι|Ϊ|Ỉ|Ị|И|Ы/' => 'I',
|
||||
'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|η|ή|ί|ι|ϊ|ỉ|ị|и|ы|ї/' => 'i',
|
||||
'/Ĵ/' => 'J',
|
||||
'/ĵ/' => 'j',
|
||||
'/Ķ|Κ|К/' => 'K',
|
||||
'/ķ|κ|к/' => 'k',
|
||||
'/Ĺ|Ļ|Ľ|Ŀ|Ł|Λ|Л/' => 'L',
|
||||
'/ĺ|ļ|ľ|ŀ|ł|λ|л/' => 'l',
|
||||
'/М/' => 'M',
|
||||
'/м/' => 'm',
|
||||
'/Ñ|Ń|Ņ|Ň|Ν|Н/' => 'N',
|
||||
'/ñ|ń|ņ|ň|ʼn|ν|н/' => 'n',
|
||||
'/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ|Ο|Ό|Ω|Ώ|Ỏ|Ọ|Ồ|Ố|Ỗ|Ổ|Ộ|Ờ|Ớ|Ỡ|Ở|Ợ|О/' => 'O',
|
||||
'/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º|ο|ό|ω|ώ|ỏ|ọ|ồ|ố|ỗ|ổ|ộ|ờ|ớ|ỡ|ở|ợ|о/' => 'o',
|
||||
'/П/' => 'P',
|
||||
'/п/' => 'p',
|
||||
'/Ŕ|Ŗ|Ř|Ρ|Р/' => 'R',
|
||||
'/ŕ|ŗ|ř|ρ|р/' => 'r',
|
||||
'/Ś|Ŝ|Ş|Ș|Š|Σ|С/' => 'S',
|
||||
'/ś|ŝ|ş|ș|š|ſ|σ|ς|с/' => 's',
|
||||
'/Ț|Ţ|Ť|Ŧ|τ|Т/' => 'T',
|
||||
'/ț|ţ|ť|ŧ|т/' => 't',
|
||||
'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|Ũ|Ủ|Ụ|Ừ|Ứ|Ữ|Ử|Ự|У/' => 'U',
|
||||
'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|υ|ύ|ϋ|ủ|ụ|ừ|ứ|ữ|ử|ự|у/' => 'u',
|
||||
'/Ƴ|Ɏ|Ỵ|Ẏ|Ӳ|Ӯ|Ў|Ý|Ÿ|Ŷ|Υ|Ύ|Ϋ|Ỳ|Ỹ|Ỷ|Ỵ|Й/' => 'Y',
|
||||
'/ẙ|ʏ|ƴ|ɏ|ỵ|ẏ|ӳ|ӯ|ў|ý|ÿ|ŷ|ỳ|ỹ|ỷ|ỵ|й/' => 'y',
|
||||
'/В/' => 'V',
|
||||
'/в/' => 'v',
|
||||
'/Ŵ/' => 'W',
|
||||
'/ŵ/' => 'w',
|
||||
'/Ź|Ż|Ž|Ζ|З/' => 'Z',
|
||||
'/ź|ż|ž|ζ|з/' => 'z',
|
||||
'/Æ|Ǽ/' => 'AE',
|
||||
'/ß/' => 'ss',
|
||||
'/IJ/' => 'IJ',
|
||||
'/ij/' => 'ij',
|
||||
'/Œ/' => 'OE',
|
||||
'/ƒ/' => 'f',
|
||||
'/ξ/' => 'ks',
|
||||
'/π/' => 'p',
|
||||
'/β/' => 'v',
|
||||
'/μ/' => 'm',
|
||||
'/ψ/' => 'ps',
|
||||
'/Ё/' => 'Yo',
|
||||
'/ё/' => 'yo',
|
||||
'/Є/' => 'Ye',
|
||||
'/є/' => 'ye',
|
||||
'/Ї/' => 'Yi',
|
||||
'/Ж/' => 'Zh',
|
||||
'/ж/' => 'zh',
|
||||
'/Х/' => 'Kh',
|
||||
'/х/' => 'kh',
|
||||
'/Ц/' => 'Ts',
|
||||
'/ц/' => 'ts',
|
||||
'/Ч/' => 'Ch',
|
||||
'/ч/' => 'ch',
|
||||
'/Ш/' => 'Sh',
|
||||
'/ш/' => 'sh',
|
||||
'/Щ/' => 'Shch',
|
||||
'/щ/' => 'shch',
|
||||
'/Ъ|ъ|Ь|ь/' => '',
|
||||
'/Ю/' => 'Yu',
|
||||
'/ю/' => 'yu',
|
||||
'/Я/' => 'Ya',
|
||||
'/я/' => 'ya',
|
||||
];
|
||||
|
||||
}
|
||||
66
system/Config/Routes.php
Normal file
66
system/Config/Routes.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||
|
||||
/**
|
||||
* System URI Routing
|
||||
*
|
||||
* This file contains any routing to system tools, such as command-line
|
||||
* tools for migrations, etc.
|
||||
*
|
||||
* It is called by Config\Routes, and has the $routes RouteCollection
|
||||
* already loaded up and ready for us to use.
|
||||
*/
|
||||
// Prevent access to BaseController
|
||||
$routes->add('basecontroller(:any)', function () {
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
});
|
||||
|
||||
// Migrations
|
||||
$routes->cli('migrations/(:segment)/(:segment)', '\CodeIgniter\Commands\MigrationsCommand::$1/$2');
|
||||
$routes->cli('migrations/(:segment)', '\CodeIgniter\Commands\MigrationsCommand::$1');
|
||||
$routes->cli('migrations', '\CodeIgniter\Commands\MigrationsCommand::index');
|
||||
|
||||
// CLI Catchall - uses a _remap to call Commands
|
||||
$routes->cli('ci(:any)', '\CodeIgniter\CLI\CommandRunner::index/$1');
|
||||
|
||||
// Prevent access to initController method
|
||||
$routes->add('(:any)/initController', function () {
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
});
|
||||
930
system/Config/Services.php
Normal file
930
system/Config/Services.php
Normal file
@@ -0,0 +1,930 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Config;
|
||||
|
||||
use CodeIgniter\Cache\CacheFactory;
|
||||
use CodeIgniter\Debug\Exceptions;
|
||||
use CodeIgniter\Debug\Iterator;
|
||||
use CodeIgniter\Debug\Timer;
|
||||
use CodeIgniter\Debug\Toolbar;
|
||||
use CodeIgniter\Encryption\EncrypterInterface;
|
||||
use CodeIgniter\Encryption\Encryption;
|
||||
use CodeIgniter\Filters\Filters;
|
||||
use CodeIgniter\Honeypot\Honeypot;
|
||||
use CodeIgniter\HTTP\CLIRequest;
|
||||
use CodeIgniter\HTTP\CURLRequest;
|
||||
use CodeIgniter\HTTP\IncomingRequest;
|
||||
use CodeIgniter\HTTP\Negotiate;
|
||||
use CodeIgniter\HTTP\RedirectResponse;
|
||||
use CodeIgniter\HTTP\Request;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\Response;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use CodeIgniter\HTTP\URI;
|
||||
use CodeIgniter\HTTP\UserAgent;
|
||||
use CodeIgniter\Language\Language;
|
||||
use CodeIgniter\Pager\Pager;
|
||||
use CodeIgniter\Router\RouteCollection;
|
||||
use CodeIgniter\Router\RouteCollectionInterface;
|
||||
use CodeIgniter\Router\Router;
|
||||
use CodeIgniter\Security\Security;
|
||||
use CodeIgniter\Session\Session;
|
||||
use CodeIgniter\Throttle\Throttler;
|
||||
use CodeIgniter\Typography\Typography;
|
||||
use CodeIgniter\Validation\Validation;
|
||||
use CodeIgniter\View\Cell;
|
||||
use CodeIgniter\View\Parser;
|
||||
use Config\App;
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Database\MigrationRunner;
|
||||
use CodeIgniter\View\RendererInterface;
|
||||
use Config\Cache;
|
||||
use Config\Images;
|
||||
use Config\Logger;
|
||||
use Config\Migrations;
|
||||
|
||||
/**
|
||||
* Services Configuration file.
|
||||
*
|
||||
* Services are simply other classes/libraries that the system uses
|
||||
* to do its job. This is used by CodeIgniter to allow the core of the
|
||||
* framework to be swapped out easily without affecting the usage within
|
||||
* the rest of your application.
|
||||
*
|
||||
* This is used in place of a Dependency Injection container primarily
|
||||
* due to its simplicity, which allows a better long-term maintenance
|
||||
* of the applications built on top of CodeIgniter. A bonus side-effect
|
||||
* is that IDEs are able to determine what class you are calling
|
||||
* whereas with DI Containers there usually isn't a way for them to do this.
|
||||
*
|
||||
* @see http://blog.ircmaxell.com/2015/11/simple-easy-risk-and-change.html
|
||||
* @see http://www.infoq.com/presentations/Simple-Made-Easy
|
||||
*/
|
||||
class Services extends BaseService
|
||||
{
|
||||
|
||||
/**
|
||||
* The cache class provides a simple way to store and retrieve
|
||||
* complex data for later.
|
||||
*
|
||||
* @param \Config\Cache $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Cache\CacheInterface
|
||||
*/
|
||||
public static function cache(Cache $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('cache', $config);
|
||||
}
|
||||
|
||||
if (! is_object($config))
|
||||
{
|
||||
$config = new Cache();
|
||||
}
|
||||
|
||||
return CacheFactory::getHandler($config);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The CLI Request class provides for ways to interact with
|
||||
* a command line request.
|
||||
*
|
||||
* @param \Config\App $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\HTTP\CLIRequest
|
||||
*/
|
||||
public static function clirequest(App $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('clirequest', $config);
|
||||
}
|
||||
|
||||
if (! is_object($config))
|
||||
{
|
||||
$config = config(App::class);
|
||||
}
|
||||
|
||||
return new CLIRequest($config);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The CURL Request class acts as a simple HTTP client for interacting
|
||||
* with other servers, typically through APIs.
|
||||
*
|
||||
* @param array $options
|
||||
* @param \CodeIgniter\HTTP\ResponseInterface $response
|
||||
* @param \Config\App $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\HTTP\CURLRequest
|
||||
*/
|
||||
public static function curlrequest(array $options = [], ResponseInterface $response = null, App $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared === true)
|
||||
{
|
||||
return static::getSharedInstance('curlrequest', $options, $response, $config);
|
||||
}
|
||||
|
||||
if (! is_object($config))
|
||||
{
|
||||
$config = config(App::class);
|
||||
}
|
||||
|
||||
if (! is_object($response))
|
||||
{
|
||||
$response = new Response($config);
|
||||
}
|
||||
|
||||
return new CURLRequest(
|
||||
$config,
|
||||
new URI($options['base_uri'] ?? null),
|
||||
$response,
|
||||
$options
|
||||
);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Email class allows you to send email via mail, sendmail, SMTP.
|
||||
*
|
||||
* @param null $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Email\Email|mixed
|
||||
*/
|
||||
public static function email($config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('email', $config);
|
||||
}
|
||||
if (empty($config))
|
||||
{
|
||||
$config = new \Config\Email();
|
||||
}
|
||||
$email = new \CodeIgniter\Email\Email($config);
|
||||
$email->setLogger(static::logger(true));
|
||||
return $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Encryption class provides two-way encryption.
|
||||
*
|
||||
* @param mixed $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return EncrypterInterface Encryption handler
|
||||
*/
|
||||
public static function encrypter($config = null, $getShared = false)
|
||||
{
|
||||
if ($getShared === true)
|
||||
{
|
||||
return static::getSharedInstance('encrypter', $config);
|
||||
}
|
||||
|
||||
if (empty($config))
|
||||
{
|
||||
$config = new \Config\Encryption();
|
||||
}
|
||||
|
||||
$encryption = new Encryption($config);
|
||||
$encrypter = $encryption->initialize($config);
|
||||
return $encrypter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Exceptions class holds the methods that handle:
|
||||
*
|
||||
* - set_exception_handler
|
||||
* - set_error_handler
|
||||
* - register_shutdown_function
|
||||
*
|
||||
* @param \Config\Exceptions $config
|
||||
* @param \CodeIgniter\HTTP\IncomingRequest $request
|
||||
* @param \CodeIgniter\HTTP\Response $response
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Debug\Exceptions
|
||||
*/
|
||||
public static function exceptions(
|
||||
\Config\Exceptions $config = null,
|
||||
IncomingRequest $request = null,
|
||||
Response $response = null,
|
||||
bool $getShared = true
|
||||
)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('exceptions', $config, $request, $response);
|
||||
}
|
||||
|
||||
if (empty($config))
|
||||
{
|
||||
$config = new \Config\Exceptions();
|
||||
}
|
||||
|
||||
if (empty($request))
|
||||
{
|
||||
$request = static::request();
|
||||
}
|
||||
|
||||
if (empty($response))
|
||||
{
|
||||
$response = static::response();
|
||||
}
|
||||
|
||||
return (new Exceptions($config, $request, $response));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Filters allow you to run tasks before and/or after a controller
|
||||
* is executed. During before filters, the request can be modified,
|
||||
* and actions taken based on the request, while after filters can
|
||||
* act on or modify the response itself before it is sent to the client.
|
||||
*
|
||||
* @param mixed $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Filters\Filters
|
||||
*/
|
||||
public static function filters($config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('filters', $config);
|
||||
}
|
||||
|
||||
if (empty($config))
|
||||
{
|
||||
$config = new \Config\Filters();
|
||||
}
|
||||
|
||||
return new Filters($config, static::request(), static::response());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Honeypot provides a secret input on forms that bots should NOT
|
||||
* fill in, providing an additional safeguard when accepting user input.
|
||||
*
|
||||
* @param \CodeIgniter\Config\BaseConfig|null $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Honeypot\Honeypot|mixed
|
||||
*/
|
||||
public static function honeypot(BaseConfig $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('honeypot', $config);
|
||||
}
|
||||
|
||||
if (is_null($config))
|
||||
{
|
||||
$config = new \Config\Honeypot();
|
||||
}
|
||||
|
||||
return new Honeypot($config);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Acts as a factory for ImageHandler classes and returns an instance
|
||||
* of the handler. Used like Services::image()->withFile($path)->rotate(90)->save();
|
||||
*
|
||||
* @param string $handler
|
||||
* @param mixed $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Images\Handlers\BaseHandler
|
||||
*/
|
||||
public static function image(string $handler = null, $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('image', $handler, $config);
|
||||
}
|
||||
|
||||
if (empty($config))
|
||||
{
|
||||
$config = new Images();
|
||||
}
|
||||
|
||||
$handler = is_null($handler) ? $config->defaultHandler : $handler;
|
||||
|
||||
$class = $config->handlers[$handler];
|
||||
|
||||
return new $class($config);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Iterator class provides a simple way of looping over a function
|
||||
* and timing the results and memory usage. Used when debugging and
|
||||
* optimizing applications.
|
||||
*
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Debug\Iterator
|
||||
*/
|
||||
public static function iterator(bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('iterator');
|
||||
}
|
||||
|
||||
return new Iterator();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Responsible for loading the language string translations.
|
||||
*
|
||||
* @param string $locale
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Language\Language
|
||||
*/
|
||||
public static function language(string $locale = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('language', $locale)
|
||||
->setLocale($locale);
|
||||
}
|
||||
|
||||
$locale = ! empty($locale) ? $locale : static::request()
|
||||
->getLocale();
|
||||
|
||||
return new Language($locale);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Logger class is a PSR-3 compatible Logging class that supports
|
||||
* multiple handlers that process the actual logging.
|
||||
*
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Log\Logger
|
||||
*/
|
||||
public static function logger(bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('logger');
|
||||
}
|
||||
|
||||
return new \CodeIgniter\Log\Logger(new Logger());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return the appropriate igration runner.
|
||||
*
|
||||
* @param \CodeIgniter\Config\BaseConfig $config
|
||||
* @param \CodeIgniter\Database\ConnectionInterface $db
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Database\MigrationRunner
|
||||
*/
|
||||
public static function migrations(BaseConfig $config = null, ConnectionInterface $db = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('migrations', $config, $db);
|
||||
}
|
||||
|
||||
$config = empty($config) ? new Migrations() : $config;
|
||||
|
||||
return new MigrationRunner($config, $db);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Negotiate class provides the content negotiation features for
|
||||
* working the request to determine correct language, encoding, charset,
|
||||
* and more.
|
||||
*
|
||||
* @param \CodeIgniter\HTTP\RequestInterface $request
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\HTTP\Negotiate
|
||||
*/
|
||||
public static function negotiator(RequestInterface $request = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('negotiator', $request);
|
||||
}
|
||||
|
||||
if (is_null($request))
|
||||
{
|
||||
$request = static::request();
|
||||
}
|
||||
|
||||
return new Negotiate($request);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return the appropriate pagination handler.
|
||||
*
|
||||
* @param mixed $config
|
||||
* @param \CodeIgniter\View\RendererInterface $view
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Pager\Pager
|
||||
*/
|
||||
public static function pager($config = null, RendererInterface $view = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('pager', $config, $view);
|
||||
}
|
||||
|
||||
if (empty($config))
|
||||
{
|
||||
$config = new \Config\Pager();
|
||||
}
|
||||
|
||||
if (! $view instanceof RendererInterface)
|
||||
{
|
||||
$view = static::renderer();
|
||||
}
|
||||
|
||||
return new Pager($config, $view);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Parser is a simple template parser.
|
||||
*
|
||||
* @param string $viewPath
|
||||
* @param mixed $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\View\Parser
|
||||
*/
|
||||
public static function parser(string $viewPath = null, $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('parser', $viewPath, $config);
|
||||
}
|
||||
|
||||
if (is_null($config))
|
||||
{
|
||||
$config = new \Config\View();
|
||||
}
|
||||
|
||||
if (is_null($viewPath))
|
||||
{
|
||||
$paths = config('Paths');
|
||||
$viewPath = $paths->viewDirectory;
|
||||
}
|
||||
|
||||
return new Parser($config, $viewPath, static::locator(true), CI_DEBUG, static::logger(true));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Renderer class is the class that actually displays a file to the user.
|
||||
* The default View class within CodeIgniter is intentionally simple, but this
|
||||
* service could easily be replaced by a template engine if the user needed to.
|
||||
*
|
||||
* @param string $viewPath
|
||||
* @param mixed $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\View\View
|
||||
*/
|
||||
public static function renderer(string $viewPath = null, $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('renderer', $viewPath, $config);
|
||||
}
|
||||
|
||||
if (is_null($config))
|
||||
{
|
||||
$config = new \Config\View();
|
||||
}
|
||||
|
||||
if (is_null($viewPath))
|
||||
{
|
||||
$paths = config('Paths');
|
||||
|
||||
$viewPath = $paths->viewDirectory;
|
||||
}
|
||||
|
||||
return new \CodeIgniter\View\View($config, $viewPath, static::locator(true), CI_DEBUG, static::logger(true));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Request class models an HTTP request.
|
||||
*
|
||||
* @param \Config\App $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\HTTP\IncomingRequest
|
||||
*/
|
||||
public static function request(App $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('request', $config);
|
||||
}
|
||||
|
||||
if (! is_object($config))
|
||||
{
|
||||
$config = config(App::class);
|
||||
}
|
||||
|
||||
return new IncomingRequest(
|
||||
$config,
|
||||
new URI(),
|
||||
'php://input',
|
||||
new UserAgent()
|
||||
);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Response class models an HTTP response.
|
||||
*
|
||||
* @param \Config\App $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\HTTP\Response
|
||||
*/
|
||||
public static function response(App $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('response', $config);
|
||||
}
|
||||
|
||||
if (! is_object($config))
|
||||
{
|
||||
$config = config(App::class);
|
||||
}
|
||||
|
||||
return new Response($config);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Redirect class provides nice way of working with redirects.
|
||||
*
|
||||
* @param \Config\App $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\HTTP\Response
|
||||
*/
|
||||
public static function redirectResponse(App $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('redirectResponse', $config);
|
||||
}
|
||||
|
||||
if (! is_object($config))
|
||||
{
|
||||
$config = config(App::class);
|
||||
}
|
||||
|
||||
$response = new RedirectResponse($config);
|
||||
$response->setProtocolVersion(static::request()
|
||||
->getProtocolVersion());
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Routes service is a class that allows for easily building
|
||||
* a collection of routes.
|
||||
*
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Router\RouteCollection
|
||||
*/
|
||||
public static function routes(bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('routes');
|
||||
}
|
||||
|
||||
return new RouteCollection(static::locator(), config('Modules'));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Router class uses a RouteCollection's array of routes, and determines
|
||||
* the correct Controller and Method to execute.
|
||||
*
|
||||
* @param \CodeIgniter\Router\RouteCollectionInterface $routes
|
||||
* @param \CodeIgniter\HTTP\Request $request
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Router\Router
|
||||
*/
|
||||
public static function router(RouteCollectionInterface $routes = null, Request $request = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('router', $routes, $request);
|
||||
}
|
||||
|
||||
if (empty($routes))
|
||||
{
|
||||
$routes = static::routes(true);
|
||||
}
|
||||
|
||||
return new Router($routes, $request);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Security class provides a few handy tools for keeping the site
|
||||
* secure, most notably the CSRF protection tools.
|
||||
*
|
||||
* @param \Config\App $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Security\Security
|
||||
*/
|
||||
public static function security(App $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('security', $config);
|
||||
}
|
||||
|
||||
if (! is_object($config))
|
||||
{
|
||||
$config = config(App::class);
|
||||
}
|
||||
|
||||
return new Security($config);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return the session manager.
|
||||
*
|
||||
* @param \Config\App $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Session\Session
|
||||
*/
|
||||
public static function session(App $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('session', $config);
|
||||
}
|
||||
|
||||
if (! is_object($config))
|
||||
{
|
||||
$config = config(App::class);
|
||||
}
|
||||
|
||||
$logger = static::logger(true);
|
||||
|
||||
$driverName = $config->sessionDriver;
|
||||
$driver = new $driverName($config, static::request()->getIpAddress());
|
||||
$driver->setLogger($logger);
|
||||
|
||||
$session = new Session($driver, $config);
|
||||
$session->setLogger($logger);
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE)
|
||||
{
|
||||
$session->start();
|
||||
}
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Throttler class provides a simple method for implementing
|
||||
* rate limiting in your applications.
|
||||
*
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Throttle\Throttler
|
||||
*/
|
||||
public static function throttler(bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('throttler');
|
||||
}
|
||||
|
||||
return new Throttler(static::cache());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Timer class provides a simple way to Benchmark portions of your
|
||||
* application.
|
||||
*
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Debug\Timer
|
||||
*/
|
||||
public static function timer(bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('timer');
|
||||
}
|
||||
|
||||
return new Timer();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return the debug toolbar.
|
||||
*
|
||||
* @param \Config\Toolbar $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Debug\Toolbar
|
||||
*/
|
||||
public static function toolbar(\Config\Toolbar $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('toolbar', $config);
|
||||
}
|
||||
|
||||
if (! is_object($config))
|
||||
{
|
||||
$config = config('Toolbar');
|
||||
}
|
||||
|
||||
return new Toolbar($config);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The URI class provides a way to model and manipulate URIs.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\HTTP\URI
|
||||
*/
|
||||
public static function uri(string $uri = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('uri', $uri);
|
||||
}
|
||||
|
||||
return new URI($uri);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Validation class provides tools for validating input data.
|
||||
*
|
||||
* @param \Config\Validation $config
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Validation\Validation
|
||||
*/
|
||||
public static function validation(\Config\Validation $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('validation', $config);
|
||||
}
|
||||
|
||||
if (is_null($config))
|
||||
{
|
||||
$config = config('Validation');
|
||||
}
|
||||
|
||||
return new Validation($config, static::renderer());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* View cells are intended to let you insert HTML into view
|
||||
* that has been generated by any callable in the system.
|
||||
*
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\View\Cell
|
||||
*/
|
||||
public static function viewcell(bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('viewcell');
|
||||
}
|
||||
|
||||
return new Cell(static::cache());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Typography class provides a way to format text in semantically relevant ways.
|
||||
*
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Typography\Typography
|
||||
*/
|
||||
public static function typography(bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
return static::getSharedInstance('typography');
|
||||
}
|
||||
|
||||
return new Typography();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
107
system/Config/View.php
Normal file
107
system/Config/View.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Config;
|
||||
|
||||
/**
|
||||
* View configuration
|
||||
*/
|
||||
class View extends BaseConfig
|
||||
{
|
||||
|
||||
/**
|
||||
* Built-in View filters.
|
||||
*
|
||||
* @var type
|
||||
*/
|
||||
protected $coreFilters = [
|
||||
'abs' => '\abs',
|
||||
'capitalize' => '\CodeIgniter\View\Filters::capitalize',
|
||||
'date' => '\CodeIgniter\View\Filters::date',
|
||||
'date_modify' => '\CodeIgniter\View\Filters::date_modify',
|
||||
'default' => '\CodeIgniter\View\Filters::default',
|
||||
'esc' => '\CodeIgniter\View\Filters::esc',
|
||||
'excerpt' => '\CodeIgniter\View\Filters::excerpt',
|
||||
'highlight' => '\CodeIgniter\View\Filters::highlight',
|
||||
'highlight_code' => '\CodeIgniter\View\Filters::highlight_code',
|
||||
'limit_words' => '\CodeIgniter\View\Filters::limit_words',
|
||||
'limit_chars' => '\CodeIgniter\View\Filters::limit_chars',
|
||||
'local_currency' => '\CodeIgniter\View\Filters::local_currency',
|
||||
'local_number' => '\CodeIgniter\View\Filters::local_number',
|
||||
'lower' => '\strtolower',
|
||||
'nl2br' => '\CodeIgniter\View\Filters::nl2br',
|
||||
'number_format' => '\number_format',
|
||||
'prose' => '\CodeIgniter\View\Filters::prose',
|
||||
'round' => '\CodeIgniter\View\Filters::round',
|
||||
'strip_tags' => '\strip_tags',
|
||||
'title' => '\CodeIgniter\View\Filters::title',
|
||||
'upper' => '\strtoupper',
|
||||
];
|
||||
|
||||
/**
|
||||
* Built-in View plugins.
|
||||
*
|
||||
* @var type
|
||||
*/
|
||||
protected $corePlugins = [
|
||||
'current_url' => '\CodeIgniter\View\Plugins::currentURL',
|
||||
'previous_url' => '\CodeIgniter\View\Plugins::previousURL',
|
||||
'mailto' => '\CodeIgniter\View\Plugins::mailto',
|
||||
'safe_mailto' => '\CodeIgniter\View\Plugins::safeMailto',
|
||||
'lang' => '\CodeIgniter\View\Plugins::lang',
|
||||
'validation_errors' => '\CodeIgniter\View\Plugins::validationErrors',
|
||||
'route' => '\CodeIgniter\View\Plugins::route',
|
||||
'siteURL' => '\CodeIgniter\View\Plugins::siteURL',
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Merge the built-in and developer-configured filters and plugins,
|
||||
* with preference to the developer ones.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->filters = array_merge($this->coreFilters, $this->filters);
|
||||
$this->plugins = array_merge($this->corePlugins, $this->plugins);
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
}
|
||||
226
system/Controller.php
Normal file
226
system/Controller.php
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter;
|
||||
|
||||
use CodeIgniter\Config\Services;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use CodeIgniter\Validation\Validation;
|
||||
use CodeIgniter\Validation\Exceptions\ValidationException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Class Controller
|
||||
*
|
||||
* @package CodeIgniter
|
||||
*/
|
||||
class Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* An array of helpers to be automatically loaded
|
||||
* upon class instantiation.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $helpers = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Instance of the main Request object.
|
||||
*
|
||||
* @var HTTP\IncomingRequest
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Instance of the main response object.
|
||||
*
|
||||
* @var HTTP\Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Instance of logger to use.
|
||||
*
|
||||
* @var Log\Logger
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Whether HTTPS access should be enforced
|
||||
* for all methods in this controller.
|
||||
*
|
||||
* @var integer Number of seconds to set HSTS header
|
||||
*/
|
||||
protected $forceHTTPS = 0;
|
||||
|
||||
/**
|
||||
* Once validation has been run,
|
||||
* will hold the Validation instance.
|
||||
*
|
||||
* @var Validation
|
||||
*/
|
||||
protected $validator;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @throws \CodeIgniter\HTTP\Exceptions\HTTPException
|
||||
*/
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
$this->logger = $logger;
|
||||
$this->logger->info('Controller "' . get_class($this) . '" loaded.');
|
||||
|
||||
if ($this->forceHTTPS > 0)
|
||||
{
|
||||
$this->forceHTTPS($this->forceHTTPS);
|
||||
}
|
||||
|
||||
$this->loadHelpers();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A convenience method to use when you need to ensure that a single
|
||||
* method is reached only via HTTPS. If it isn't, then a redirect
|
||||
* will happen back to this method and HSTS header will be sent
|
||||
* to have modern browsers transform requests automatically.
|
||||
*
|
||||
* @param integer $duration The number of seconds this link should be
|
||||
* considered secure for. Only with HSTS header.
|
||||
* Default value is 1 year.
|
||||
*
|
||||
* @throws \CodeIgniter\HTTP\Exceptions\HTTPException
|
||||
*/
|
||||
protected function forceHTTPS(int $duration = 31536000)
|
||||
{
|
||||
force_https($duration, $this->request, $this->response);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Provides a simple way to tie into the main CodeIgniter class
|
||||
* and tell it how long to cache the current page for.
|
||||
*
|
||||
* @param integer $time
|
||||
*/
|
||||
protected function cachePage(int $time)
|
||||
{
|
||||
CodeIgniter::cache($time);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Handles "auto-loading" helper files.
|
||||
*/
|
||||
protected function loadHelpers()
|
||||
{
|
||||
if (empty($this->helpers))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->helpers as $helper)
|
||||
{
|
||||
helper($helper);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A shortcut to performing validation on input data. If validation
|
||||
* is not successful, a $errors property will be set on this class.
|
||||
*
|
||||
* @param array|string $rules
|
||||
* @param array $messages An array of custom error messages
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function validate($rules, array $messages = []): bool
|
||||
{
|
||||
$this->validator = Services::validation();
|
||||
|
||||
// If you replace the $rules array with the name of the group
|
||||
if (is_string($rules))
|
||||
{
|
||||
$validation = new \Config\Validation();
|
||||
|
||||
// If the rule wasn't found in the \Config\Validation, we
|
||||
// should throw an exception so the developer can find it.
|
||||
if (! isset($validation->$rules))
|
||||
{
|
||||
throw ValidationException::forRuleNotFound($rules);
|
||||
}
|
||||
|
||||
// If no error message is defined, use the error message in the Config\Validation file
|
||||
if (! $messages)
|
||||
{
|
||||
$errorName = $rules . '_errors';
|
||||
$messages = $validation->$errorName ?? [];
|
||||
}
|
||||
|
||||
$rules = $validation->$rules;
|
||||
}
|
||||
|
||||
$success = $this->validator
|
||||
->withRequest($this->request)
|
||||
->setRules($rules, $messages)
|
||||
->run();
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
3352
system/Database/BaseBuilder.php
Normal file
3352
system/Database/BaseBuilder.php
Normal file
File diff suppressed because it is too large
Load Diff
1882
system/Database/BaseConnection.php
Normal file
1882
system/Database/BaseConnection.php
Normal file
File diff suppressed because it is too large
Load Diff
274
system/Database/BasePreparedQuery.php
Normal file
274
system/Database/BasePreparedQuery.php
Normal file
@@ -0,0 +1,274 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database;
|
||||
|
||||
use CodeIgniter\Database\MySQLi\Connection;
|
||||
use CodeIgniter\Events\Events;
|
||||
|
||||
/**
|
||||
* Base prepared query
|
||||
*/
|
||||
abstract class BasePreparedQuery implements PreparedQueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* The prepared statement itself.
|
||||
*
|
||||
* @var resource|\mysqli_stmt
|
||||
*/
|
||||
protected $statement;
|
||||
|
||||
/**
|
||||
* The error code, if any.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $errorCode;
|
||||
|
||||
/**
|
||||
* The error message, if any.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $errorString;
|
||||
|
||||
/**
|
||||
* Holds the prepared query object
|
||||
* that is cloned during execute.
|
||||
*
|
||||
* @var Query
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* A reference to the db connection to use.
|
||||
*
|
||||
* @var BaseConnection|MySQLi\Connection
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \CodeIgniter\Database\ConnectionInterface $db
|
||||
*/
|
||||
public function __construct(ConnectionInterface $db)
|
||||
{
|
||||
$this->db = &$db;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepares the query against the database, and saves the connection
|
||||
* info necessary to execute the query later.
|
||||
*
|
||||
* NOTE: This version is based on SQL code. Child classes should
|
||||
* override this method.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $options Passed to the connection's prepare statement.
|
||||
* @param string $queryClass
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function prepare(string $sql, array $options = [], string $queryClass = 'CodeIgniter\\Database\\Query')
|
||||
{
|
||||
// We only supports positional placeholders (?)
|
||||
// in order to work with the execute method below, so we
|
||||
// need to replace our named placeholders (:name)
|
||||
$sql = preg_replace('/:[^\s,)]+/', '?', $sql);
|
||||
|
||||
/**
|
||||
* @var \CodeIgniter\Database\Query $query
|
||||
*/
|
||||
$query = new $queryClass($this->db);
|
||||
|
||||
$query->setQuery($sql);
|
||||
|
||||
if (! empty($this->db->swapPre) && ! empty($this->db->DBPrefix))
|
||||
{
|
||||
$query->swapPrefix($this->db->DBPrefix, $this->db->swapPre);
|
||||
}
|
||||
|
||||
$this->query = $query;
|
||||
|
||||
return $this->_prepare($query->getOriginalQuery(), $options);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The database-dependent portion of the prepare statement.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $options Passed to the connection's prepare statement.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function _prepare(string $sql, array $options = []);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes a new set of data and runs it against the currently
|
||||
* prepared query. Upon success, will return a Results object.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return ResultInterface
|
||||
*/
|
||||
public function execute(...$data)
|
||||
{
|
||||
// Execute the Query.
|
||||
$startTime = microtime(true);
|
||||
|
||||
$result = $this->_execute($data);
|
||||
|
||||
// Update our query object
|
||||
$query = clone $this->query;
|
||||
$query->setBinds($data);
|
||||
|
||||
$query->setDuration($startTime);
|
||||
|
||||
// Let others do something with this query
|
||||
Events::trigger('DBQuery', $query);
|
||||
|
||||
// Return a result object
|
||||
$resultClass = str_replace('PreparedQuery', 'Result', get_class($this));
|
||||
|
||||
$resultID = $this->_getResult();
|
||||
|
||||
return new $resultClass($this->db->connID, $resultID);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The database dependant version of the execute method.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function _execute(array $data): bool;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result object for the prepared query.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function _getResult();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Explicitly closes the statement.
|
||||
*
|
||||
* @return null|void
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if (! is_object($this->statement))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->statement->close();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the SQL that has been prepared.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryString(): string
|
||||
{
|
||||
if (! $this->query instanceof QueryInterface)
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot call getQueryString on a prepared query until after the query has been prepared.');
|
||||
}
|
||||
|
||||
return $this->query->getQuery();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A helper to determine if any error exists.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasError(): bool
|
||||
{
|
||||
return ! empty($this->errorString);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error code created while executing this statement.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getErrorCode(): int
|
||||
{
|
||||
return $this->errorCode;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error message created while executing this statement.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMessage(): string
|
||||
{
|
||||
return $this->errorString;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
629
system/Database/BaseResult.php
Normal file
629
system/Database/BaseResult.php
Normal file
@@ -0,0 +1,629 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database;
|
||||
|
||||
/**
|
||||
* Class BaseResult
|
||||
*/
|
||||
abstract class BaseResult implements ResultInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Connection ID
|
||||
*
|
||||
* @var resource|object
|
||||
*/
|
||||
public $connID;
|
||||
|
||||
/**
|
||||
* Result ID
|
||||
*
|
||||
* @var resource|object
|
||||
*/
|
||||
public $resultID;
|
||||
|
||||
/**
|
||||
* Result Array
|
||||
*
|
||||
* @var array[]
|
||||
*/
|
||||
public $resultArray = [];
|
||||
|
||||
/**
|
||||
* Result Object
|
||||
*
|
||||
* @var object[]
|
||||
*/
|
||||
public $resultObject = [];
|
||||
|
||||
/**
|
||||
* Custom Result Object
|
||||
*
|
||||
* @var object[]
|
||||
*/
|
||||
public $customResultObject = [];
|
||||
|
||||
/**
|
||||
* Current Row index
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $currentRow = 0;
|
||||
|
||||
/**
|
||||
* Number of rows
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $numRows;
|
||||
|
||||
/**
|
||||
* Row data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $rowData;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param object|resource $connID
|
||||
* @param object|resource $resultID
|
||||
*/
|
||||
public function __construct(&$connID, &$resultID)
|
||||
{
|
||||
$this->connID = $connID;
|
||||
$this->resultID = $resultID;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Retrieve the results of the query. Typically an array of
|
||||
* individual data rows, which can be either an 'array', an
|
||||
* 'object', or a custom class name.
|
||||
*
|
||||
* @param string $type The row type. Either 'array', 'object', or a class name to use
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResult(string $type = 'object'): array
|
||||
{
|
||||
if ($type === 'array')
|
||||
{
|
||||
return $this->getResultArray();
|
||||
}
|
||||
elseif ($type === 'object')
|
||||
{
|
||||
return $this->getResultObject();
|
||||
}
|
||||
|
||||
return $this->getCustomResultObject($type);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the results as an array of custom objects.
|
||||
*
|
||||
* @param string $className The name of the class to use.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCustomResultObject(string $className)
|
||||
{
|
||||
if (isset($this->customResultObject[$className]))
|
||||
{
|
||||
return $this->customResultObject[$className];
|
||||
}
|
||||
|
||||
if (is_bool($this->resultID) || ! $this->resultID || $this->numRows === 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
// Don't fetch the result set again if we already have it
|
||||
$_data = null;
|
||||
if (($c = count($this->resultArray)) > 0)
|
||||
{
|
||||
$_data = 'result_array';
|
||||
}
|
||||
elseif (($c = count($this->resultObject)) > 0)
|
||||
{
|
||||
$_data = 'result_object';
|
||||
}
|
||||
|
||||
if ($_data !== null)
|
||||
{
|
||||
for ($i = 0; $i < $c; $i ++)
|
||||
{
|
||||
$this->customResultObject[$className][$i] = new $className();
|
||||
|
||||
foreach ($this->{$_data}[$i] as $key => $value)
|
||||
{
|
||||
$this->customResultObject[$className][$i]->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->customResultObject[$className];
|
||||
}
|
||||
|
||||
is_null($this->rowData) || $this->dataSeek(0);
|
||||
$this->customResultObject[$className] = [];
|
||||
|
||||
while ($row = $this->fetchObject($className))
|
||||
{
|
||||
if (method_exists($row, 'syncOriginal'))
|
||||
{
|
||||
$row->syncOriginal();
|
||||
}
|
||||
|
||||
$this->customResultObject[$className][] = $row;
|
||||
}
|
||||
|
||||
return $this->customResultObject[$className];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the results as an array of arrays.
|
||||
*
|
||||
* If no results, an empty array is returned.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResultArray(): array
|
||||
{
|
||||
if (! empty($this->resultArray))
|
||||
{
|
||||
return $this->resultArray;
|
||||
}
|
||||
|
||||
// In the event that query caching is on, the result_id variable
|
||||
// will not be a valid resource so we'll simply return an empty
|
||||
// array.
|
||||
if (is_bool($this->resultID) || ! $this->resultID || $this->numRows === 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($this->resultObject)
|
||||
{
|
||||
foreach ($this->resultObject as $row)
|
||||
{
|
||||
$this->resultArray[] = (array) $row;
|
||||
}
|
||||
|
||||
return $this->resultArray;
|
||||
}
|
||||
|
||||
is_null($this->rowData) || $this->dataSeek(0);
|
||||
while ($row = $this->fetchAssoc())
|
||||
{
|
||||
$this->resultArray[] = $row;
|
||||
}
|
||||
|
||||
return $this->resultArray;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the results as an array of objects.
|
||||
*
|
||||
* If no results, an empty array is returned.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResultObject(): array
|
||||
{
|
||||
if (! empty($this->resultObject))
|
||||
{
|
||||
return $this->resultObject;
|
||||
}
|
||||
|
||||
// In the event that query caching is on, the result_id variable
|
||||
// will not be a valid resource so we'll simply return an empty
|
||||
// array.
|
||||
if (is_bool($this->resultID) || ! $this->resultID || $this->numRows === 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($this->resultArray)
|
||||
{
|
||||
foreach ($this->resultArray as $row)
|
||||
{
|
||||
$this->resultObject[] = (object) $row;
|
||||
}
|
||||
|
||||
return $this->resultObject;
|
||||
}
|
||||
|
||||
is_null($this->rowData) || $this->dataSeek(0);
|
||||
while ($row = $this->fetchObject())
|
||||
{
|
||||
if (method_exists($row, 'syncOriginal'))
|
||||
{
|
||||
$row->syncOriginal();
|
||||
}
|
||||
|
||||
$this->resultObject[] = $row;
|
||||
}
|
||||
|
||||
return $this->resultObject;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Wrapper object to return a row as either an array, an object, or
|
||||
* a custom class.
|
||||
*
|
||||
* If row doesn't exist, returns null.
|
||||
*
|
||||
* @param mixed $n The index of the results to return
|
||||
* @param string $type The type of result object. 'array', 'object' or class name.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRow($n = 0, string $type = 'object')
|
||||
{
|
||||
if (! is_numeric($n))
|
||||
{
|
||||
// We cache the row data for subsequent uses
|
||||
is_array($this->rowData) || $this->rowData = $this->getRowArray(0);
|
||||
|
||||
// array_key_exists() instead of isset() to allow for NULL values
|
||||
if (empty($this->rowData) || ! array_key_exists($n, $this->rowData))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->rowData[$n];
|
||||
}
|
||||
|
||||
if ($type === 'object')
|
||||
{
|
||||
return $this->getRowObject($n);
|
||||
}
|
||||
elseif ($type === 'array')
|
||||
{
|
||||
return $this->getRowArray($n);
|
||||
}
|
||||
|
||||
return $this->getCustomRowObject($n, $type);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a row as a custom class instance.
|
||||
*
|
||||
* If row doesn't exists, returns null.
|
||||
*
|
||||
* @param integer $n
|
||||
* @param string $className
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCustomRowObject(int $n, string $className)
|
||||
{
|
||||
isset($this->customResultObject[$className]) || $this->getCustomResultObject($className);
|
||||
|
||||
if (empty($this->customResultObject[$className]))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($n !== $this->currentRow && isset($this->customResultObject[$className][$n]))
|
||||
{
|
||||
$this->currentRow = $n;
|
||||
}
|
||||
|
||||
return $this->customResultObject[$className][$this->currentRow];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a single row from the results as an array.
|
||||
*
|
||||
* If row doesn't exist, returns null.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRowArray(int $n = 0)
|
||||
{
|
||||
$result = $this->getResultArray();
|
||||
if (empty($result))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($n !== $this->currentRow && isset($result[$n]))
|
||||
{
|
||||
$this->currentRow = $n;
|
||||
}
|
||||
|
||||
return $result[$this->currentRow];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a single row from the results as an object.
|
||||
*
|
||||
* If row doesn't exist, returns null.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRowObject(int $n = 0)
|
||||
{
|
||||
$result = $this->getResultObject();
|
||||
if (empty($result))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($n !== $this->customResultObject && isset($result[$n]))
|
||||
{
|
||||
$this->currentRow = $n;
|
||||
}
|
||||
|
||||
return $result[$this->currentRow];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Assigns an item into a particular column slot.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setRow($key, $value = null)
|
||||
{
|
||||
// We cache the row data for subsequent uses
|
||||
if (! is_array($this->rowData))
|
||||
{
|
||||
$this->rowData = $this->getRowArray(0);
|
||||
}
|
||||
|
||||
if (is_array($key))
|
||||
{
|
||||
foreach ($key as $k => $v)
|
||||
{
|
||||
$this->rowData[$k] = $v;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($key !== '' && $value !== null)
|
||||
{
|
||||
$this->rowData[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "first" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFirstRow(string $type = 'object')
|
||||
{
|
||||
$result = $this->getResult($type);
|
||||
|
||||
return (empty($result)) ? null : $result[0];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "last" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLastRow(string $type = 'object')
|
||||
{
|
||||
$result = $this->getResult($type);
|
||||
|
||||
return (empty($result)) ? null : $result[count($result) - 1];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "next" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNextRow(string $type = 'object')
|
||||
{
|
||||
$result = $this->getResult($type);
|
||||
if (empty($result))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return isset($result[$this->currentRow + 1]) ? $result[++ $this->currentRow] : null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "previous" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPreviousRow(string $type = 'object')
|
||||
{
|
||||
$result = $this->getResult($type);
|
||||
if (empty($result))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isset($result[$this->currentRow - 1]))
|
||||
{
|
||||
-- $this->currentRow;
|
||||
}
|
||||
|
||||
return $result[$this->currentRow];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an unbuffered row and move the pointer to the next row.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUnbufferedRow(string $type = 'object')
|
||||
{
|
||||
if ($type === 'array')
|
||||
{
|
||||
return $this->fetchAssoc();
|
||||
}
|
||||
elseif ($type === 'object')
|
||||
{
|
||||
return $this->fetchObject();
|
||||
}
|
||||
|
||||
return $this->fetchObject($type);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets the number of fields in the result set.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
abstract public function getFieldCount(): int;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of column names in the result set.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getFieldNames(): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of objects representing field meta-data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getFieldData(): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Frees the current result.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function freeResult();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Moves the internal pointer to the desired offset. This is called
|
||||
* internally before fetching results to make sure the result set
|
||||
* starts at zero.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function dataSeek(int $n = 0);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an array.
|
||||
*
|
||||
* Overridden by driver classes.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function fetchAssoc();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an object.
|
||||
*
|
||||
* Overridden by child classes.
|
||||
*
|
||||
* @param string $className
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
abstract protected function fetchObject(string $className = 'stdClass');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
458
system/Database/BaseUtils.php
Normal file
458
system/Database/BaseUtils.php
Normal file
@@ -0,0 +1,458 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database;
|
||||
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Class BaseUtils
|
||||
*/
|
||||
abstract class BaseUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* Database object
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* List databases statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $listDatabases = false;
|
||||
|
||||
/**
|
||||
* OPTIMIZE TABLE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $optimizeTable = false;
|
||||
|
||||
/**
|
||||
* REPAIR TABLE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $repairTable = false;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param ConnectionInterface|object $db
|
||||
*/
|
||||
public function __construct(ConnectionInterface &$db)
|
||||
{
|
||||
$this->db = & $db;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* List databases
|
||||
*
|
||||
* @return array|boolean
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function listDatabases()
|
||||
{
|
||||
// Is there a cached result?
|
||||
if (isset($this->db->dataCache['db_names']))
|
||||
{
|
||||
return $this->db->dataCache['db_names'];
|
||||
}
|
||||
elseif ($this->listDatabases === false)
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('Unsupported feature of the database platform you are using.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->dataCache['db_names'] = [];
|
||||
|
||||
$query = $this->db->query($this->listDatabases);
|
||||
if ($query === false)
|
||||
{
|
||||
return $this->db->dataCache['db_names'];
|
||||
}
|
||||
|
||||
for ($i = 0, $query = $query->getResultArray(), $c = count($query); $i < $c; $i ++)
|
||||
{
|
||||
$this->db->dataCache['db_names'][] = current($query[$i]);
|
||||
}
|
||||
|
||||
return $this->db->dataCache['db_names'];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determine if a particular database exists
|
||||
*
|
||||
* @param string $database_name
|
||||
* @return boolean
|
||||
*/
|
||||
public function databaseExists(string $database_name): bool
|
||||
{
|
||||
return in_array($database_name, $this->listDatabases());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Optimize Table
|
||||
*
|
||||
* @param string $table_name
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function optimizeTable(string $table_name)
|
||||
{
|
||||
if ($this->optimizeTable === false)
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('Unsupported feature of the database platform you are using.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = $this->db->query(sprintf($this->optimizeTable, $this->db->escapeIdentifiers($table_name)));
|
||||
if ($query !== false)
|
||||
{
|
||||
$query = $query->getResultArray();
|
||||
return current($query);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Optimize Database
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function optimizeDatabase()
|
||||
{
|
||||
if ($this->optimizeTable === false)
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('Unsupported feature of the database platform you are using.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($this->db->listTables() as $table_name)
|
||||
{
|
||||
$res = $this->db->query(sprintf($this->optimizeTable, $this->db->escapeIdentifiers($table_name)));
|
||||
if (is_bool($res))
|
||||
{
|
||||
return $res;
|
||||
}
|
||||
|
||||
// Build the result array...
|
||||
|
||||
$res = $res->getResultArray();
|
||||
|
||||
// Postgre & SQLite3 returns empty array
|
||||
if (empty($res))
|
||||
{
|
||||
$key = $table_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
$res = current($res);
|
||||
$key = str_replace($this->db->database . '.', '', current($res));
|
||||
$keys = array_keys($res);
|
||||
unset($res[$keys[0]]);
|
||||
}
|
||||
|
||||
$result[$key] = $res;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Repair Table
|
||||
*
|
||||
* @param string $table_name
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function repairTable(string $table_name)
|
||||
{
|
||||
if ($this->repairTable === false)
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('Unsupported feature of the database platform you are using.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = $this->db->query(sprintf($this->repairTable, $this->db->escapeIdentifiers($table_name)));
|
||||
if (is_bool($query))
|
||||
{
|
||||
return $query;
|
||||
}
|
||||
|
||||
$query = $query->getResultArray();
|
||||
return current($query);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generate CSV from a query result object
|
||||
*
|
||||
* @param ResultInterface $query Query result object
|
||||
* @param string $delim Delimiter (default: ,)
|
||||
* @param string $newline Newline character (default: \n)
|
||||
* @param string $enclosure Enclosure (default: ")
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCSVFromResult(ResultInterface $query, string $delim = ',', string $newline = "\n", string $enclosure = '"')
|
||||
{
|
||||
$out = '';
|
||||
// First generate the headings from the table column names
|
||||
foreach ($query->getFieldNames() as $name)
|
||||
{
|
||||
$out .= $enclosure . str_replace($enclosure, $enclosure . $enclosure, $name) . $enclosure . $delim;
|
||||
}
|
||||
|
||||
$out = substr($out, 0, -strlen($delim)) . $newline;
|
||||
|
||||
// Next blast through the result array and build out the rows
|
||||
while ($row = $query->getUnbufferedRow('array'))
|
||||
{
|
||||
$line = [];
|
||||
foreach ($row as $item)
|
||||
{
|
||||
$line[] = $enclosure . str_replace($enclosure, $enclosure . $enclosure, $item) . $enclosure;
|
||||
}
|
||||
$out .= implode($delim, $line) . $newline;
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generate XML data from a query result object
|
||||
*
|
||||
* @param ResultInterface $query Query result object
|
||||
* @param array $params Any preferences
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getXMLFromResult(ResultInterface $query, array $params = []): string
|
||||
{
|
||||
// Set our default values
|
||||
foreach (['root' => 'root', 'element' => 'element', 'newline' => "\n", 'tab' => "\t"] as $key => $val)
|
||||
{
|
||||
if (! isset($params[$key]))
|
||||
{
|
||||
$params[$key] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
// Create variables for convenience
|
||||
extract($params);
|
||||
|
||||
// Load the xml helper
|
||||
helper('xml');
|
||||
// Generate the result
|
||||
$xml = '<' . $root . '>' . $newline;
|
||||
while ($row = $query->getUnbufferedRow())
|
||||
{
|
||||
$xml .= $tab . '<' . $element . '>' . $newline;
|
||||
foreach ($row as $key => $val)
|
||||
{
|
||||
$val = (! empty($val)) ? xml_convert($val) : '';
|
||||
$xml .= $tab . $tab . '<' . $key . '>' . $val . '</' . $key . '>' . $newline;
|
||||
}
|
||||
$xml .= $tab . '</' . $element . '>' . $newline;
|
||||
}
|
||||
|
||||
return $xml . '</' . $root . '>' . $newline;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Database Backup
|
||||
*
|
||||
* @param array|string $params
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function backup($params = [])
|
||||
{
|
||||
// If the parameters have not been submitted as an
|
||||
// array then we know that it is simply the table
|
||||
// name, which is a valid short cut.
|
||||
if (is_string($params))
|
||||
{
|
||||
$params = ['tables' => $params];
|
||||
}
|
||||
|
||||
// Set up our default preferences
|
||||
$prefs = [
|
||||
'tables' => [],
|
||||
'ignore' => [],
|
||||
'filename' => '',
|
||||
'format' => 'gzip', // gzip, zip, txt
|
||||
'add_drop' => true,
|
||||
'add_insert' => true,
|
||||
'newline' => "\n",
|
||||
'foreign_key_checks' => true,
|
||||
];
|
||||
|
||||
// Did the user submit any preferences? If so set them....
|
||||
if (! empty($params))
|
||||
{
|
||||
foreach ($prefs as $key => $val)
|
||||
{
|
||||
if (isset($params[$key]))
|
||||
{
|
||||
$prefs[$key] = $params[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Are we backing up a complete database or individual tables?
|
||||
// If no table names were submitted we'll fetch the entire table list
|
||||
if (empty($prefs['tables']))
|
||||
{
|
||||
$prefs['tables'] = $this->db->listTables();
|
||||
}
|
||||
|
||||
// Validate the format
|
||||
if (! in_array($prefs['format'], ['gzip', 'zip', 'txt'], true))
|
||||
{
|
||||
$prefs['format'] = 'txt';
|
||||
}
|
||||
|
||||
// Is the encoder supported? If not, we'll either issue an
|
||||
// error or use plain text depending on the debug settings
|
||||
if (($prefs['format'] === 'gzip' && ! function_exists('gzencode'))
|
||||
|| ( $prefs['format'] === 'zip' && ! function_exists('gzcompress')))
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('The file compression format you chose is not supported by your server.');
|
||||
}
|
||||
|
||||
$prefs['format'] = 'txt';
|
||||
}
|
||||
|
||||
// Was a Zip file requested?
|
||||
if ($prefs['format'] === 'zip')
|
||||
{
|
||||
// Set the filename if not provided (only needed with Zip files)
|
||||
if ($prefs['filename'] === '')
|
||||
{
|
||||
$prefs['filename'] = (count($prefs['tables']) === 1 ? $prefs['tables'] : $this->db->database)
|
||||
. date('Y-m-d_H-i', time()) . '.sql';
|
||||
}
|
||||
else
|
||||
{
|
||||
// If they included the .zip file extension we'll remove it
|
||||
if (preg_match('|.+?\.zip$|', $prefs['filename']))
|
||||
{
|
||||
$prefs['filename'] = str_replace('.zip', '', $prefs['filename']);
|
||||
}
|
||||
|
||||
// Tack on the ".sql" file extension if needed
|
||||
if (! preg_match('|.+?\.sql$|', $prefs['filename']))
|
||||
{
|
||||
$prefs['filename'] .= '.sql';
|
||||
}
|
||||
}
|
||||
|
||||
// Load the Zip class and output it
|
||||
// $CI =& get_instance();
|
||||
// $CI->load->library('zip');
|
||||
// $CI->zip->add_data($prefs['filename'], $this->_backup($prefs));
|
||||
// return $CI->zip->get_zip();
|
||||
}
|
||||
elseif ($prefs['format'] === 'txt') // Was a text file requested?
|
||||
{
|
||||
return $this->_backup($prefs);
|
||||
}
|
||||
elseif ($prefs['format'] === 'gzip') // Was a Gzip file requested?
|
||||
{
|
||||
return gzencode($this->_backup($prefs));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform dependent version of the backup function.
|
||||
*
|
||||
* @param array|null $prefs
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function _backup(array $prefs = null);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
199
system/Database/Config.php
Normal file
199
system/Database/Config.php
Normal file
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
/**
|
||||
* Class Config
|
||||
*/
|
||||
class Config extends BaseConfig
|
||||
{
|
||||
|
||||
/**
|
||||
* Cache for instance of any connections that
|
||||
* have been requested as a "shared" instance.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static protected $instances = [];
|
||||
|
||||
/**
|
||||
* The main instance used to manage all of
|
||||
* our open database connections.
|
||||
*
|
||||
* @var \CodeIgniter\Database\Database
|
||||
*/
|
||||
static protected $factory;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates the default
|
||||
*
|
||||
* @param string|array $group The name of the connection group to use,
|
||||
* or an array of configuration settings.
|
||||
* @param boolean $getShared Whether to return a shared instance of the connection.
|
||||
*
|
||||
* @return BaseConnection
|
||||
*/
|
||||
public static function connect($group = null, bool $getShared = true)
|
||||
{
|
||||
// If a DB connection is passed in, just pass it back
|
||||
if ($group instanceof BaseConnection)
|
||||
{
|
||||
return $group;
|
||||
}
|
||||
|
||||
if (is_array($group))
|
||||
{
|
||||
$config = $group;
|
||||
$group = 'custom-' . md5(json_encode($config));
|
||||
}
|
||||
|
||||
$config = $config ?? config('Database');
|
||||
|
||||
if (empty($group))
|
||||
{
|
||||
$group = ENVIRONMENT === 'testing' ? 'tests' : $config->defaultGroup;
|
||||
}
|
||||
|
||||
if (is_string($group) && ! isset($config->$group) && strpos($group, 'custom-') !== 0)
|
||||
{
|
||||
throw new \InvalidArgumentException($group . ' is not a valid database connection group.');
|
||||
}
|
||||
|
||||
if ($getShared && isset(static::$instances[$group]))
|
||||
{
|
||||
return static::$instances[$group];
|
||||
}
|
||||
|
||||
static::ensureFactory();
|
||||
|
||||
if (isset($config->$group))
|
||||
{
|
||||
$config = $config->$group;
|
||||
}
|
||||
|
||||
$connection = static::$factory->load($config, $group);
|
||||
|
||||
static::$instances[$group] = & $connection;
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of all db connections currently made.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getConnections(): array
|
||||
{
|
||||
return static::$instances;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Loads and returns an instance of the Forge for the specified
|
||||
* database group, and loads the group if it hasn't been loaded yet.
|
||||
*
|
||||
* @param string|array|null $group
|
||||
*
|
||||
* @return Forge
|
||||
*/
|
||||
public static function forge($group = null)
|
||||
{
|
||||
$db = static::connect($group);
|
||||
|
||||
return static::$factory->loadForge($db);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a new instance of the Database Utilities class.
|
||||
*
|
||||
* @param string|array|null $group
|
||||
*
|
||||
* @return BaseUtils
|
||||
*/
|
||||
public static function utils($group = null)
|
||||
{
|
||||
$db = static::connect($group);
|
||||
|
||||
return static::$factory->loadUtils($db);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a new instance of the Database Seeder.
|
||||
*
|
||||
* @param string|null $group
|
||||
*
|
||||
* @return Seeder
|
||||
*/
|
||||
public static function seeder(string $group = null)
|
||||
{
|
||||
$config = config('Database');
|
||||
|
||||
return new Seeder($config, static::connect($group));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Ensures the database Connection Manager/Factory is loaded and ready to use.
|
||||
*/
|
||||
protected static function ensureFactory()
|
||||
{
|
||||
if (static::$factory instanceof Database)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static::$factory = new Database();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
225
system/Database/ConnectionInterface.php
Normal file
225
system/Database/ConnectionInterface.php
Normal file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database;
|
||||
|
||||
/**
|
||||
* Interface ConnectionInterface
|
||||
*
|
||||
* @package CodeIgniter\Database
|
||||
*/
|
||||
interface ConnectionInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Initializes the database connection/settings.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function initialize();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Connect to the database.
|
||||
*
|
||||
* @param boolean $persistent
|
||||
* @return mixed
|
||||
*/
|
||||
public function connect(bool $persistent = false);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a persistent database connection.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function persistentConnect();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Keep or establish the connection if no queries have been sent for
|
||||
* a length of time exceeding the server's idle timeout.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function reconnect();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the actual connection object. If both a 'read' and 'write'
|
||||
* connection has been specified, you can pass either term in to
|
||||
* get that connection. If you pass either alias in and only a single
|
||||
* connection is present, it must return the sole connection.
|
||||
*
|
||||
* @param string|null $alias
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConnection(string $alias = null);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Select a specific database table to use.
|
||||
*
|
||||
* @param string $databaseName
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setDatabase(string $databaseName);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the name of the current database being used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabase(): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the last error encountered by this connection.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getError();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The name of the platform in use (MySQLi, mssql, etc)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPlatform(): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a string containing the version of the database being used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion(): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Orchestrates a query against the database. Queries must use
|
||||
* Database\Statement objects to store the query and build it.
|
||||
* This method works with the cache.
|
||||
*
|
||||
* Should automatically handle different connections for read/write
|
||||
* queries if needed.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param mixed ...$binds
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function query(string $sql, $binds = null);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs a basic query against the database. No binding or caching
|
||||
* is performed, nor are transactions handled. Simply takes a raw
|
||||
* query string and returns the database-specific result id.
|
||||
*
|
||||
* @param string $sql
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function simpleQuery(string $sql);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an instance of the query builder for this connection.
|
||||
*
|
||||
* @param string|array $tableName Table name.
|
||||
*
|
||||
* @return BaseBuilder Builder.
|
||||
*/
|
||||
public function table($tableName);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the last query's statement object.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLastQuery();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* "Smart" Escaping
|
||||
*
|
||||
* Escapes data based on type.
|
||||
* Sets boolean and null types.
|
||||
*
|
||||
* @param mixed $str
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function escape($str);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Allows for custom calls to the database engine that are not
|
||||
* supported through our database layer.
|
||||
*
|
||||
* @param string $functionName
|
||||
* @param array ...$params
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function callFunction(string $functionName, ...$params);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
142
system/Database/Database.php
Normal file
142
system/Database/Database.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database;
|
||||
|
||||
/**
|
||||
* Database Connection Factory
|
||||
*
|
||||
* Creates and returns an instance of the appropriate DatabaseConnection
|
||||
*
|
||||
* @package CodeIgniter\Database
|
||||
*/
|
||||
class Database
|
||||
{
|
||||
|
||||
/**
|
||||
* Maintains an array of the instances of all connections
|
||||
* that have been created. Helps to keep track of all open
|
||||
* connections for performance monitoring, logging, etc.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $connections = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parses the connection binds and returns an instance of
|
||||
* the driver ready to go.
|
||||
*
|
||||
* @param array $params
|
||||
* @param string $alias
|
||||
*
|
||||
* @return mixed
|
||||
* @internal param bool $useBuilder
|
||||
*/
|
||||
public function load(array $params = [], string $alias)
|
||||
{
|
||||
// No DB specified? Beat them senseless...
|
||||
if (empty($params['DBDriver']))
|
||||
{
|
||||
throw new \InvalidArgumentException('You have not selected a database type to connect to.');
|
||||
}
|
||||
|
||||
$className = strpos($params['DBDriver'], '\\') === false
|
||||
? '\CodeIgniter\Database\\' . $params['DBDriver'] . '\\Connection'
|
||||
: $params['DBDriver'] . '\\Connection';
|
||||
|
||||
$class = new $className($params);
|
||||
|
||||
// Store the connection
|
||||
$this->connections[$alias] = $class;
|
||||
|
||||
return $this->connections[$alias];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a new Forge instance for the current database type.
|
||||
*
|
||||
* @param ConnectionInterface|BaseConnection $db
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function loadForge(ConnectionInterface $db)
|
||||
{
|
||||
$className = strpos($db->DBDriver, '\\') === false ? '\CodeIgniter\Database\\' . $db->DBDriver . '\\Forge' : $db->DBDriver . '\\Forge';
|
||||
|
||||
// Make sure a connection exists
|
||||
if (! $db->connID)
|
||||
{
|
||||
$db->initialize();
|
||||
}
|
||||
|
||||
$class = new $className($db);
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Loads the Database Utilities class.
|
||||
*
|
||||
* @param ConnectionInterface|BaseConnection $db
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function loadUtils(ConnectionInterface $db)
|
||||
{
|
||||
$className = strpos($db->DBDriver, '\\') === false ? '\CodeIgniter\Database\\' . $db->DBDriver . '\\Utils' : $db->DBDriver . '\\Utils';
|
||||
|
||||
// Make sure a connection exists
|
||||
if (! $db->connID)
|
||||
{
|
||||
$db->initialize();
|
||||
}
|
||||
|
||||
$class = new $className($db);
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
65
system/Database/Exceptions/DataException.php
Normal file
65
system/Database/Exceptions/DataException.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace CodeIgniter\Database\Exceptions;
|
||||
|
||||
class DataException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
/**
|
||||
* Used by the Model's trigger() method when the callback cannot be found.
|
||||
*
|
||||
* @param string $method
|
||||
*
|
||||
* @return \CodeIgniter\Database\Exceptions\DataException
|
||||
*/
|
||||
public static function forInvalidMethodTriggered(string $method)
|
||||
{
|
||||
return new static(lang('Database.invalidEvent', [$method]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by Model's insert/update methods when there isn't
|
||||
* any data to actually work with.
|
||||
*
|
||||
* @param string $mode
|
||||
*
|
||||
* @return \CodeIgniter\Database\Exceptions\DataException
|
||||
*/
|
||||
public static function forEmptyDataset(string $mode)
|
||||
{
|
||||
return new static(lang('Database.emptyDataset', [$mode]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when an argument for one of the Model's methods
|
||||
* were empty or otherwise invalid, and they could not be
|
||||
* to work correctly for that method.
|
||||
*
|
||||
* @param string $argument
|
||||
*
|
||||
* @return \CodeIgniter\Database\Exceptions\DataException
|
||||
*/
|
||||
public static function forInvalidArgument(string $argument)
|
||||
{
|
||||
return new static(lang('Database.invalidArgument', [$argument]));
|
||||
}
|
||||
|
||||
public static function forInvalidAllowedFields(string $model)
|
||||
{
|
||||
return new static(lang('Database.invalidAllowedFields', [$model]));
|
||||
}
|
||||
|
||||
public static function forTableNotFound(string $table)
|
||||
{
|
||||
return new static(lang('Database.tableNotFound', [$table]));
|
||||
}
|
||||
|
||||
public static function forEmptyInputGiven(string $argument)
|
||||
{
|
||||
return new static(lang('Database.forEmptyInputGiven', [$argument]));
|
||||
}
|
||||
|
||||
public static function forFindColumnHaveMultipleColumns()
|
||||
{
|
||||
return new static(lang('Database.forFindColumnHaveMultipleColumns'));
|
||||
}
|
||||
}
|
||||
51
system/Database/Exceptions/DatabaseException.php
Normal file
51
system/Database/Exceptions/DatabaseException.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\Exceptions;
|
||||
|
||||
use CodeIgniter\Exceptions\ExceptionInterface;
|
||||
|
||||
class DatabaseException extends \Error implements ExceptionInterface
|
||||
{
|
||||
/**
|
||||
* Exit status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 8;
|
||||
}
|
||||
13
system/Database/Exceptions/ExceptionInterface.php
Normal file
13
system/Database/Exceptions/ExceptionInterface.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace CodeIgniter\Database\Exceptions;
|
||||
|
||||
/**
|
||||
* Provides a domain-level interface for broad capture
|
||||
* of all database-related exceptions.
|
||||
*
|
||||
* catch (\CodeIgniter\Database\Exceptions\ExceptionInterface) { ... }
|
||||
*/
|
||||
|
||||
interface ExceptionInterface extends \CodeIgniter\Exceptions\ExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
1321
system/Database/Forge.php
Normal file
1321
system/Database/Forge.php
Normal file
File diff suppressed because it is too large
Load Diff
110
system/Database/Migration.php
Normal file
110
system/Database/Migration.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database;
|
||||
|
||||
/**
|
||||
* Class Migration
|
||||
*/
|
||||
abstract class Migration
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of the database group to use.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $DBGroup;
|
||||
|
||||
/**
|
||||
* Database Connection instance
|
||||
*
|
||||
* @var BaseConnection
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Database Forge instance.
|
||||
*
|
||||
* @var Forge
|
||||
*/
|
||||
protected $forge;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \CodeIgniter\Database\Forge $forge
|
||||
*/
|
||||
public function __construct(Forge $forge = null)
|
||||
{
|
||||
$this->forge = ! is_null($forge) ? $forge : \Config\Database::forge($this->DBGroup ?? config('Database')->defaultGroup);
|
||||
|
||||
$this->db = $this->forge->getConnection();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the database group name this migration uses.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDBGroup(): ?string
|
||||
{
|
||||
return $this->DBGroup;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Perform a migration step.
|
||||
*/
|
||||
abstract public function up();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Revert a migration step.
|
||||
*/
|
||||
abstract public function down();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
1049
system/Database/MigrationRunner.php
Normal file
1049
system/Database/MigrationRunner.php
Normal file
File diff suppressed because it is too large
Load Diff
77
system/Database/MySQLi/Builder.php
Normal file
77
system/Database/MySQLi/Builder.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\MySQLi;
|
||||
|
||||
use CodeIgniter\Database\BaseBuilder;
|
||||
|
||||
/**
|
||||
* Builder for MySQLi
|
||||
*/
|
||||
class Builder extends BaseBuilder
|
||||
{
|
||||
|
||||
/**
|
||||
* Identifier escape character
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $escapeChar = '`';
|
||||
|
||||
/**
|
||||
* FROM tables
|
||||
*
|
||||
* Groups tables in FROM clauses if needed, so there is no confusion
|
||||
* about operator precedence.
|
||||
*
|
||||
* Note: This is only used (and overridden) by MySQL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _fromTables(): string
|
||||
{
|
||||
if (! empty($this->QBJoin) && count($this->QBFrom) > 1)
|
||||
{
|
||||
return '(' . implode(', ', $this->QBFrom) . ')';
|
||||
}
|
||||
|
||||
return implode(', ', $this->QBFrom);
|
||||
}
|
||||
|
||||
}
|
||||
732
system/Database/MySQLi/Connection.php
Normal file
732
system/Database/MySQLi/Connection.php
Normal file
@@ -0,0 +1,732 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\MySQLi;
|
||||
|
||||
use CodeIgniter\Database\BaseConnection;
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Connection for MySQLi
|
||||
*/
|
||||
class Connection extends BaseConnection implements ConnectionInterface
|
||||
{
|
||||
/**
|
||||
* Database driver
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $DBDriver = 'MySQLi';
|
||||
/**
|
||||
* DELETE hack flag
|
||||
*
|
||||
* Whether to use the MySQL "delete hack" which allows the number
|
||||
* of affected rows to be shown. Uses a preg_replace when enabled,
|
||||
* adding a bit more processing to all queries.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $deleteHack = true;
|
||||
// --------------------------------------------------------------------
|
||||
/**
|
||||
* Identifier escape character
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $escapeChar = '`';
|
||||
// --------------------------------------------------------------------
|
||||
/**
|
||||
* MySQLi object
|
||||
*
|
||||
* Has to be preserved without being assigned to $conn_id.
|
||||
*
|
||||
* @var \MySQLi
|
||||
*/
|
||||
public $mysqli;
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Connect to the database.
|
||||
*
|
||||
* @param boolean $persistent
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function connect(bool $persistent = false)
|
||||
{
|
||||
// Do we have a socket path?
|
||||
if ($this->hostname[0] === '/')
|
||||
{
|
||||
$hostname = null;
|
||||
$port = null;
|
||||
$socket = $this->hostname;
|
||||
}
|
||||
else
|
||||
{
|
||||
$hostname = ($persistent === true) ? 'p:' . $this->hostname : $this->hostname;
|
||||
$port = empty($this->port) ? null : $this->port;
|
||||
$socket = null;
|
||||
}
|
||||
|
||||
$client_flags = ($this->compress === true) ? MYSQLI_CLIENT_COMPRESS : 0;
|
||||
$this->mysqli = mysqli_init();
|
||||
|
||||
mysqli_report(MYSQLI_REPORT_ALL & ~MYSQLI_REPORT_INDEX);
|
||||
|
||||
$this->mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 10);
|
||||
|
||||
if (isset($this->strictOn))
|
||||
{
|
||||
if ($this->strictOn)
|
||||
{
|
||||
$this->mysqli->options(MYSQLI_INIT_COMMAND,
|
||||
'SET SESSION sql_mode = CONCAT(@@sql_mode, ",", "STRICT_ALL_TABLES")');
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->mysqli->options(MYSQLI_INIT_COMMAND, 'SET SESSION sql_mode =
|
||||
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
|
||||
@@sql_mode,
|
||||
"STRICT_ALL_TABLES,", ""),
|
||||
",STRICT_ALL_TABLES", ""),
|
||||
"STRICT_ALL_TABLES", ""),
|
||||
"STRICT_TRANS_TABLES,", ""),
|
||||
",STRICT_TRANS_TABLES", ""),
|
||||
"STRICT_TRANS_TABLES", "")'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($this->encrypt))
|
||||
{
|
||||
$ssl = [];
|
||||
empty($this->encrypt['ssl_key']) || $ssl['key'] = $this->encrypt['ssl_key'];
|
||||
empty($this->encrypt['ssl_cert']) || $ssl['cert'] = $this->encrypt['ssl_cert'];
|
||||
empty($this->encrypt['ssl_ca']) || $ssl['ca'] = $this->encrypt['ssl_ca'];
|
||||
empty($this->encrypt['ssl_capath']) || $ssl['capath'] = $this->encrypt['ssl_capath'];
|
||||
empty($this->encrypt['ssl_cipher']) || $ssl['cipher'] = $this->encrypt['ssl_cipher'];
|
||||
|
||||
if (! empty($ssl))
|
||||
{
|
||||
if (isset($this->encrypt['ssl_verify']))
|
||||
{
|
||||
if ($this->encrypt['ssl_verify'])
|
||||
{
|
||||
defined('MYSQLI_OPT_SSL_VERIFY_SERVER_CERT') &&
|
||||
$this->mysqli->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, true);
|
||||
}
|
||||
// Apparently (when it exists), setting MYSQLI_OPT_SSL_VERIFY_SERVER_CERT
|
||||
// to FALSE didn't do anything, so PHP 5.6.16 introduced yet another
|
||||
// constant ...
|
||||
//
|
||||
// https://secure.php.net/ChangeLog-5.php#5.6.16
|
||||
// https://bugs.php.net/bug.php?id=68344
|
||||
elseif (defined('MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT') && version_compare($this->mysqli->client_info, '5.6', '>='))
|
||||
{
|
||||
$client_flags += MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
|
||||
}
|
||||
}
|
||||
|
||||
$client_flags += MYSQLI_CLIENT_SSL;
|
||||
$this->mysqli->ssl_set(
|
||||
$ssl['key'] ?? null, $ssl['cert'] ?? null, $ssl['ca'] ?? null,
|
||||
$ssl['capath'] ?? null, $ssl['cipher'] ?? null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if ($this->mysqli->real_connect($hostname, $this->username, $this->password,
|
||||
$this->database, $port, $socket, $client_flags)
|
||||
)
|
||||
{
|
||||
// Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails
|
||||
if (($client_flags & MYSQLI_CLIENT_SSL) && version_compare($this->mysqli->client_info, '5.7.3', '<=')
|
||||
&& empty($this->mysqli->query("SHOW STATUS LIKE 'ssl_cipher'")
|
||||
->fetch_object()->Value)
|
||||
)
|
||||
{
|
||||
$this->mysqli->close();
|
||||
$message = 'MySQLi was configured for an SSL connection, but got an unencrypted connection instead!';
|
||||
log_message('error', $message);
|
||||
|
||||
if ($this->DBDebug)
|
||||
{
|
||||
throw new DatabaseException($message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->mysqli->set_charset($this->charset))
|
||||
{
|
||||
log_message('error',
|
||||
"Database: Unable to set the configured connection charset ('{$this->charset}').");
|
||||
$this->mysqli->close();
|
||||
|
||||
if ($this->db->debug)
|
||||
{
|
||||
throw new DatabaseException('Unable to set client connection character set: ' . $this->charset);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->mysqli;
|
||||
}
|
||||
}
|
||||
catch (\Throwable $e)
|
||||
{
|
||||
// Clean sensitive information from errors.
|
||||
$msg = $e->getMessage();
|
||||
|
||||
$msg = str_replace($this->username, '****', $msg);
|
||||
$msg = str_replace($this->password, '****', $msg);
|
||||
|
||||
throw new \mysqli_sql_exception($msg, $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Keep or establish the connection if no queries have been sent for
|
||||
* a length of time exceeding the server's idle timeout.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reconnect()
|
||||
{
|
||||
$this->close();
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Close the database connection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _close()
|
||||
{
|
||||
$this->connID->close();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Select a specific database table to use.
|
||||
*
|
||||
* @param string $databaseName
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function setDatabase(string $databaseName): bool
|
||||
{
|
||||
if ($databaseName === '')
|
||||
{
|
||||
$databaseName = $this->database;
|
||||
}
|
||||
|
||||
if (empty($this->connID))
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
if ($this->connID->select_db($databaseName))
|
||||
{
|
||||
$this->database = $databaseName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a string containing the version of the database being used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion(): string
|
||||
{
|
||||
if (isset($this->dataCache['version']))
|
||||
{
|
||||
return $this->dataCache['version'];
|
||||
}
|
||||
|
||||
if (empty($this->mysqli))
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return $this->dataCache['version'] = $this->mysqli->server_info;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Executes the query against the database.
|
||||
*
|
||||
* @param string $sql
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function execute(string $sql)
|
||||
{
|
||||
while ($this->connID->more_results())
|
||||
{
|
||||
$this->connID->next_result();
|
||||
if ($res = $this->connID->store_result())
|
||||
{
|
||||
$res->free();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->connID->query($this->prepQuery($sql));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prep the query
|
||||
*
|
||||
* If needed, each database adapter can prep the query string
|
||||
*
|
||||
* @param string $sql an SQL query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prepQuery(string $sql): string
|
||||
{
|
||||
// mysqli_affected_rows() returns 0 for "DELETE FROM TABLE" queries. This hack
|
||||
// modifies the query so that it a proper number of affected rows is returned.
|
||||
if ($this->deleteHack === true && preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql))
|
||||
{
|
||||
return trim($sql) . ' WHERE 1=1';
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the total number of rows affected by this query.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function affectedRows(): int
|
||||
{
|
||||
return $this->connID->affected_rows ?? 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform-dependant string escape
|
||||
*
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
protected function _escapeString(string $str): string
|
||||
{
|
||||
if (is_bool($str))
|
||||
{
|
||||
return $str;
|
||||
}
|
||||
|
||||
if (! $this->connID)
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return $this->connID->real_escape_string($str);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Escape Like String Direct
|
||||
* There are a few instances where MySQLi queries cannot take the
|
||||
* additional "ESCAPE x" parameter for specifying the escape character
|
||||
* in "LIKE" strings, and this handles those directly with a backslash.
|
||||
*
|
||||
* @param string|string[] $str Input string
|
||||
* @return string|string[]
|
||||
*/
|
||||
public function escapeLikeStringDirect($str)
|
||||
{
|
||||
if (is_array($str))
|
||||
{
|
||||
foreach ($str as $key => $val)
|
||||
{
|
||||
$str[$key] = $this->escapeLikeStringDirect($val);
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
$str = $this->_escapeString($str);
|
||||
|
||||
// Escape LIKE condition wildcards
|
||||
return str_replace([
|
||||
$this->likeEscapeChar,
|
||||
'%',
|
||||
'_',
|
||||
], [
|
||||
'\\' . $this->likeEscapeChar,
|
||||
'\\' . '%',
|
||||
'\\' . '_',
|
||||
], $str
|
||||
);
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates the SQL for listing tables in a platform-dependent manner.
|
||||
* Uses escapeLikeStringDirect().
|
||||
*
|
||||
* @param boolean $prefixLimit
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _listTables(bool $prefixLimit = false): string
|
||||
{
|
||||
$sql = 'SHOW TABLES FROM ' . $this->escapeIdentifiers($this->database);
|
||||
|
||||
if ($prefixLimit !== false && $this->DBPrefix !== '')
|
||||
{
|
||||
return $sql . " LIKE '" . $this->escapeLikeStringDirect($this->DBPrefix) . "%'";
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates a platform-specific query string so that the column names can be fetched.
|
||||
*
|
||||
* @param string $table
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _listColumns(string $table = ''): string
|
||||
{
|
||||
return 'SHOW COLUMNS FROM ' . $this->protectIdentifiers($table, true, null, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with field data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _fieldData(string $table): array
|
||||
{
|
||||
$table = $this->protectIdentifiers($table, true, null, false);
|
||||
|
||||
if (($query = $this->query('SHOW COLUMNS FROM ' . $table)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetFieldData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
$retVal = [];
|
||||
for ($i = 0, $c = count($query); $i < $c; $i++)
|
||||
{
|
||||
$retVal[$i] = new \stdClass();
|
||||
$retVal[$i]->name = $query[$i]->Field;
|
||||
|
||||
sscanf($query[$i]->Type, '%[a-z](%d)', $retVal[$i]->type, $retVal[$i]->max_length);
|
||||
|
||||
$retVal[$i]->default = $query[$i]->Default;
|
||||
$retVal[$i]->primary_key = (int)($query[$i]->Key === 'PRI');
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with index data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function _indexData(string $table): array
|
||||
{
|
||||
$table = $this->protectIdentifiers($table, true, null, false);
|
||||
|
||||
if (($query = $this->query('SHOW INDEX FROM ' . $table)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetIndexData'));
|
||||
}
|
||||
|
||||
if (! $indexes = $query->getResultArray())
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$keys = [];
|
||||
|
||||
foreach ($indexes as $index)
|
||||
{
|
||||
if (empty($keys[$index['Key_name']]))
|
||||
{
|
||||
$keys[$index['Key_name']] = new \stdClass();
|
||||
$keys[$index['Key_name']]->name = $index['Key_name'];
|
||||
|
||||
if ($index['Key_name'] === 'PRIMARY')
|
||||
{
|
||||
$type = 'PRIMARY';
|
||||
}
|
||||
elseif ($index['Index_type'] === 'FULLTEXT')
|
||||
{
|
||||
$type = 'FULLTEXT';
|
||||
}
|
||||
elseif ($index['Non_unique'])
|
||||
{
|
||||
if ($index['Index_type'] === 'SPATIAL')
|
||||
{
|
||||
$type = 'SPATIAL';
|
||||
}
|
||||
else
|
||||
{
|
||||
$type = 'INDEX';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$type = 'UNIQUE';
|
||||
}
|
||||
|
||||
$keys[$index['Key_name']]->type = $type;
|
||||
}
|
||||
|
||||
$keys[$index['Key_name']]->fields[] = $index['Column_name'];
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with Foreign key data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _foreignKeyData(string $table): array
|
||||
{
|
||||
$sql = '
|
||||
SELECT
|
||||
tc.CONSTRAINT_NAME,
|
||||
tc.TABLE_NAME,
|
||||
kcu.COLUMN_NAME,
|
||||
rc.REFERENCED_TABLE_NAME,
|
||||
kcu.REFERENCED_COLUMN_NAME
|
||||
FROM information_schema.TABLE_CONSTRAINTS AS tc
|
||||
INNER JOIN information_schema.REFERENTIAL_CONSTRAINTS AS rc
|
||||
ON tc.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
|
||||
INNER JOIN information_schema.KEY_COLUMN_USAGE AS kcu
|
||||
ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
|
||||
WHERE
|
||||
tc.CONSTRAINT_TYPE = ' . $this->escape('FOREIGN KEY') . ' AND
|
||||
tc.TABLE_SCHEMA = ' . $this->escape($this->database) . ' AND
|
||||
tc.TABLE_NAME = ' . $this->escape($table);
|
||||
|
||||
if (($query = $this->query($sql)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetForeignKeyData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
$retVal = [];
|
||||
foreach ($query as $row)
|
||||
{
|
||||
$obj = new \stdClass();
|
||||
$obj->constraint_name = $row->CONSTRAINT_NAME;
|
||||
$obj->table_name = $row->TABLE_NAME;
|
||||
$obj->column_name = $row->COLUMN_NAME;
|
||||
$obj->foreign_table_name = $row->REFERENCED_TABLE_NAME;
|
||||
$obj->foreign_column_name = $row->REFERENCED_COLUMN_NAME;
|
||||
|
||||
$retVal[] = $obj;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns platform-specific SQL to disable foreign key checks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _disableForeignKeyChecks()
|
||||
{
|
||||
return 'SET FOREIGN_KEY_CHECKS=0';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns platform-specific SQL to enable foreign key checks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _enableForeignKeyChecks()
|
||||
{
|
||||
return 'SET FOREIGN_KEY_CHECKS=1';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the last error code and message.
|
||||
*
|
||||
* Must return an array with keys 'code' and 'message':
|
||||
*
|
||||
* return ['code' => null, 'message' => null);
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function error(): array
|
||||
{
|
||||
if (! empty($this->mysqli->connect_errno))
|
||||
{
|
||||
return [
|
||||
'code' => $this->mysqli->connect_errno,
|
||||
'message' => $this->mysqli->connect_error,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'code' => $this->connID->errno,
|
||||
'message' => $this->connID->error,
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Insert ID
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function insertID(): int
|
||||
{
|
||||
return $this->connID->insert_id;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Begin Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transBegin(): bool
|
||||
{
|
||||
$this->connID->autocommit(false);
|
||||
|
||||
return $this->connID->begin_transaction();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Commit Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transCommit(): bool
|
||||
{
|
||||
if ($this->connID->commit())
|
||||
{
|
||||
$this->connID->autocommit(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Rollback Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transRollback(): bool
|
||||
{
|
||||
if ($this->connID->rollback())
|
||||
{
|
||||
$this->connID->autocommit(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
279
system/Database/MySQLi/Forge.php
Normal file
279
system/Database/MySQLi/Forge.php
Normal file
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\MySQLi;
|
||||
|
||||
/**
|
||||
* Forge for MySQLi
|
||||
*/
|
||||
class Forge extends \CodeIgniter\Database\Forge
|
||||
{
|
||||
|
||||
/**
|
||||
* CREATE DATABASE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $createDatabaseStr = 'CREATE DATABASE %s CHARACTER SET %s COLLATE %s';
|
||||
|
||||
/**
|
||||
* CREATE DATABASE IF statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $createDatabaseIfStr = 'CREATE DATABASE IF NOT EXISTS %s CHARACTER SET %s COLLATE %s';
|
||||
|
||||
/**
|
||||
* DROP CONSTRAINT statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $dropConstraintStr = 'ALTER TABLE %s DROP FOREIGN KEY %s';
|
||||
|
||||
/**
|
||||
* CREATE TABLE keys flag
|
||||
*
|
||||
* Whether table keys are created from within the
|
||||
* CREATE TABLE statement.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $createTableKeys = true;
|
||||
|
||||
/**
|
||||
* UNSIGNED support
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_unsigned = [
|
||||
'TINYINT',
|
||||
'SMALLINT',
|
||||
'MEDIUMINT',
|
||||
'INT',
|
||||
'INTEGER',
|
||||
'BIGINT',
|
||||
'REAL',
|
||||
'DOUBLE',
|
||||
'DOUBLE PRECISION',
|
||||
'FLOAT',
|
||||
'DECIMAL',
|
||||
'NUMERIC',
|
||||
];
|
||||
|
||||
/**
|
||||
* Table Options list which required to be quoted
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_quoted_table_options = [
|
||||
'COMMENT',
|
||||
'COMPRESSION',
|
||||
'CONNECTION',
|
||||
'DATA DIRECTORY',
|
||||
'INDEX DIRECTORY',
|
||||
'ENCRYPTION',
|
||||
'PASSWORD',
|
||||
];
|
||||
|
||||
/**
|
||||
* NULL value representation in CREATE/ALTER TABLE statements
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_null = 'NULL';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* CREATE TABLE attributes
|
||||
*
|
||||
* @param array $attributes Associative array of table attributes
|
||||
* @return string
|
||||
*/
|
||||
protected function _createTableAttributes(array $attributes): string
|
||||
{
|
||||
$sql = '';
|
||||
|
||||
foreach (array_keys($attributes) as $key)
|
||||
{
|
||||
if (is_string($key))
|
||||
{
|
||||
$sql .= ' ' . strtoupper($key) . ' = ';
|
||||
|
||||
if (in_array(strtoupper($key), $this->_quoted_table_options))
|
||||
{
|
||||
$sql .= $this->db->escape($attributes[$key]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql .= $this->db->escapeString($attributes[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($this->db->charset) && ! strpos($sql, 'CHARACTER SET') && ! strpos($sql, 'CHARSET'))
|
||||
{
|
||||
$sql .= ' DEFAULT CHARACTER SET = ' . $this->db->escapeString($this->db->charset);
|
||||
}
|
||||
|
||||
if (! empty($this->db->DBCollat) && ! strpos($sql, 'COLLATE'))
|
||||
{
|
||||
$sql .= ' COLLATE = ' . $this->db->escapeString($this->db->DBCollat);
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ALTER TABLE
|
||||
*
|
||||
* @param string $alter_type ALTER type
|
||||
* @param string $table Table name
|
||||
* @param mixed $field Column definition
|
||||
* @return string|string[]
|
||||
*/
|
||||
protected function _alterTable(string $alter_type, string $table, $field)
|
||||
{
|
||||
if ($alter_type === 'DROP')
|
||||
{
|
||||
return parent::_alterTable($alter_type, $table, $field);
|
||||
}
|
||||
|
||||
$sql = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table);
|
||||
foreach ($field as $i => $data)
|
||||
{
|
||||
if ($data['_literal'] !== false)
|
||||
{
|
||||
$field[$i] = ($alter_type === 'ADD') ? "\n\tADD " . $data['_literal'] : "\n\tMODIFY " . $data['_literal'];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($alter_type === 'ADD')
|
||||
{
|
||||
$field[$i]['_literal'] = "\n\tADD ";
|
||||
}
|
||||
else
|
||||
{
|
||||
$field[$i]['_literal'] = empty($data['new_name']) ? "\n\tMODIFY " : "\n\tCHANGE ";
|
||||
}
|
||||
|
||||
$field[$i] = $field[$i]['_literal'] . $this->_processColumn($field[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
return [$sql . implode(',', $field)];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process column
|
||||
*
|
||||
* @param array $field
|
||||
* @return string
|
||||
*/
|
||||
protected function _processColumn(array $field): string
|
||||
{
|
||||
$extra_clause = isset($field['after']) ? ' AFTER ' . $this->db->escapeIdentifiers($field['after']) : '';
|
||||
|
||||
if (empty($extra_clause) && isset($field['first']) && $field['first'] === true)
|
||||
{
|
||||
$extra_clause = ' FIRST';
|
||||
}
|
||||
|
||||
return $this->db->escapeIdentifiers($field['name'])
|
||||
. (empty($field['new_name']) ? '' : ' ' . $this->db->escapeIdentifiers($field['new_name']))
|
||||
. ' ' . $field['type'] . $field['length']
|
||||
. $field['unsigned']
|
||||
. $field['null']
|
||||
. $field['default']
|
||||
. $field['auto_increment']
|
||||
. $field['unique']
|
||||
. (empty($field['comment']) ? '' : ' COMMENT ' . $field['comment'])
|
||||
. $extra_clause;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process indexes
|
||||
*
|
||||
* @param string $table (ignored)
|
||||
* @return string
|
||||
*/
|
||||
protected function _processIndexes(string $table): string
|
||||
{
|
||||
$sql = '';
|
||||
|
||||
for ($i = 0, $c = count($this->keys); $i < $c; $i ++)
|
||||
{
|
||||
if (is_array($this->keys[$i]))
|
||||
{
|
||||
for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2 ++)
|
||||
{
|
||||
if (! isset($this->fields[$this->keys[$i][$i2]]))
|
||||
{
|
||||
unset($this->keys[$i][$i2]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (! isset($this->fields[$this->keys[$i]]))
|
||||
{
|
||||
unset($this->keys[$i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
is_array($this->keys[$i]) || $this->keys[$i] = [$this->keys[$i]];
|
||||
|
||||
$unique = in_array($i, $this->uniqueKeys) ? 'UNIQUE ' : '';
|
||||
|
||||
$sql .= ",\n\t{$unique}KEY " . $this->db->escapeIdentifiers(implode('_', $this->keys[$i]))
|
||||
. ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ')';
|
||||
}
|
||||
|
||||
$this->keys = [];
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
137
system/Database/MySQLi/PreparedQuery.php
Normal file
137
system/Database/MySQLi/PreparedQuery.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\MySQLi;
|
||||
|
||||
use CodeIgniter\Database\PreparedQueryInterface;
|
||||
use CodeIgniter\Database\BasePreparedQuery;
|
||||
|
||||
/**
|
||||
* Prepared query for MySQLi
|
||||
*/
|
||||
class PreparedQuery extends BasePreparedQuery implements PreparedQueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Prepares the query against the database, and saves the connection
|
||||
* info necessary to execute the query later.
|
||||
*
|
||||
* NOTE: This version is based on SQL code. Child classes should
|
||||
* override this method.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $options Passed to the connection's prepare statement.
|
||||
* Unused in the MySQLi driver.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _prepare(string $sql, array $options = [])
|
||||
{
|
||||
// Mysqli driver doesn't like statements
|
||||
// with terminating semicolons.
|
||||
$sql = rtrim($sql, ';');
|
||||
|
||||
if (! $this->statement = $this->db->mysqli->prepare($sql))
|
||||
{
|
||||
$this->errorCode = $this->db->mysqli->errno;
|
||||
$this->errorString = $this->db->mysqli->error;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes a new set of data and runs it against the currently
|
||||
* prepared query. Upon success, will return a Results object.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function _execute(array $data): bool
|
||||
{
|
||||
if (is_null($this->statement))
|
||||
{
|
||||
throw new \BadMethodCallException('You must call prepare before trying to execute a prepared statement.');
|
||||
}
|
||||
|
||||
// First off -bind the parameters
|
||||
$bindTypes = '';
|
||||
|
||||
// Determine the type string
|
||||
foreach ($data as $item)
|
||||
{
|
||||
if (is_integer($item))
|
||||
{
|
||||
$bindTypes .= 'i';
|
||||
}
|
||||
elseif (is_numeric($item))
|
||||
{
|
||||
$bindTypes .= 'd';
|
||||
}
|
||||
else
|
||||
{
|
||||
$bindTypes .= 's';
|
||||
}
|
||||
}
|
||||
|
||||
// Bind it
|
||||
$this->statement->bind_param($bindTypes, ...$data);
|
||||
|
||||
$success = $this->statement->execute();
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result object for the prepared query.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _getResult()
|
||||
{
|
||||
return $this->statement->get_result();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
173
system/Database/MySQLi/Result.php
Normal file
173
system/Database/MySQLi/Result.php
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\MySQLi;
|
||||
|
||||
use CodeIgniter\Database\BaseResult;
|
||||
use CodeIgniter\Database\ResultInterface;
|
||||
use CodeIgniter\Entity;
|
||||
|
||||
/**
|
||||
* Result for MySQLi
|
||||
*/
|
||||
class Result extends BaseResult implements ResultInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets the number of fields in the result set.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getFieldCount(): int
|
||||
{
|
||||
return $this->resultID->field_count;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of column names in the result set.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldNames(): array
|
||||
{
|
||||
$fieldNames = [];
|
||||
$this->resultID->field_seek(0);
|
||||
while ($field = $this->resultID->fetch_field())
|
||||
{
|
||||
$fieldNames[] = $field->name;
|
||||
}
|
||||
|
||||
return $fieldNames;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of objects representing field meta-data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldData(): array
|
||||
{
|
||||
$retVal = [];
|
||||
$fieldData = $this->resultID->fetch_fields();
|
||||
|
||||
foreach ($fieldData as $i => $data)
|
||||
{
|
||||
$retVal[$i] = new \stdClass();
|
||||
$retVal[$i]->name = $data->name;
|
||||
$retVal[$i]->type = $data->type;
|
||||
$retVal[$i]->max_length = $data->max_length;
|
||||
$retVal[$i]->primary_key = (int) ($data->flags & 2);
|
||||
$retVal[$i]->default = $data->def;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Frees the current result.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function freeResult()
|
||||
{
|
||||
if (is_object($this->resultID))
|
||||
{
|
||||
$this->resultID->free();
|
||||
$this->resultID = false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Moves the internal pointer to the desired offset. This is called
|
||||
* internally before fetching results to make sure the result set
|
||||
* starts at zero.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function dataSeek(int $n = 0)
|
||||
{
|
||||
return $this->resultID->data_seek($n);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an array.
|
||||
*
|
||||
* Overridden by driver classes.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fetchAssoc()
|
||||
{
|
||||
return $this->resultID->fetch_assoc();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an object.
|
||||
*
|
||||
* Overridden by child classes.
|
||||
*
|
||||
* @param string $className
|
||||
*
|
||||
* @return object|boolean|Entity
|
||||
*/
|
||||
protected function fetchObject(string $className = 'stdClass')
|
||||
{
|
||||
if (is_subclass_of($className, Entity::class))
|
||||
{
|
||||
return empty($data = $this->fetchAssoc()) ? false : (new $className())->setAttributes($data);
|
||||
}
|
||||
return $this->resultID->fetch_object($className);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
80
system/Database/MySQLi/Utils.php
Normal file
80
system/Database/MySQLi/Utils.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\MySQLi;
|
||||
|
||||
use CodeIgniter\Database\BaseUtils;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Utils for MySQLi
|
||||
*/
|
||||
class Utils extends BaseUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* List databases statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $listDatabases = 'SHOW DATABASES';
|
||||
|
||||
/**
|
||||
* OPTIMIZE TABLE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $optimizeTable = 'OPTIMIZE TABLE %s';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform dependent version of the backup function.
|
||||
*
|
||||
* @param array|null $prefs
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _backup(array $prefs = null)
|
||||
{
|
||||
throw new DatabaseException('Unsupported feature of the database platform you are using.');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
368
system/Database/Postgre/Builder.php
Normal file
368
system/Database/Postgre/Builder.php
Normal file
@@ -0,0 +1,368 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\Postgre;
|
||||
|
||||
use CodeIgniter\Database\BaseBuilder;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Builder for Postgre
|
||||
*/
|
||||
class Builder extends BaseBuilder
|
||||
{
|
||||
|
||||
/**
|
||||
* ORDER BY random keyword
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $randomKeyword = [
|
||||
'RANDOM()',
|
||||
];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ORDER BY
|
||||
*
|
||||
* @param string $orderBy
|
||||
* @param string $direction ASC, DESC or RANDOM
|
||||
* @param boolean $escape
|
||||
*
|
||||
* @return BaseBuilder
|
||||
*/
|
||||
public function orderBy(string $orderBy, string $direction = '', bool $escape = null)
|
||||
{
|
||||
$direction = strtoupper(trim($direction));
|
||||
if ($direction === 'RANDOM')
|
||||
{
|
||||
if (! is_float($orderBy) && ctype_digit((string) $orderBy))
|
||||
{
|
||||
$orderBy = (float) ($orderBy > 1 ? "0.{$orderBy}" : $orderBy);
|
||||
}
|
||||
|
||||
if (is_float($orderBy))
|
||||
{
|
||||
$this->db->simpleQuery("SET SEED {$orderBy}");
|
||||
}
|
||||
|
||||
$orderBy = $this->randomKeyword[0];
|
||||
$direction = '';
|
||||
$escape = false;
|
||||
}
|
||||
|
||||
return parent::orderBy($orderBy, $direction, $escape);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Increments a numeric column by the specified value.
|
||||
*
|
||||
* @param string $column
|
||||
* @param integer $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function increment(string $column, int $value = 1)
|
||||
{
|
||||
$column = $this->db->protectIdentifiers($column);
|
||||
|
||||
$sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') + {$value}"]);
|
||||
|
||||
return $this->db->query($sql, $this->binds, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Decrements a numeric column by the specified value.
|
||||
*
|
||||
* @param string $column
|
||||
* @param integer $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decrement(string $column, int $value = 1)
|
||||
{
|
||||
$column = $this->db->protectIdentifiers($column);
|
||||
|
||||
$sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') - {$value}"]);
|
||||
|
||||
return $this->db->query($sql, $this->binds, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Replace
|
||||
*
|
||||
* Compiles an replace into string and runs the query.
|
||||
* Because PostgreSQL doesn't support the replace into command,
|
||||
* we simply do a DELETE and an INSERT on the first key/value
|
||||
* combo, assuming that it's either the primary key or a unique key.
|
||||
*
|
||||
* @param array $set An associative array of insert values
|
||||
*
|
||||
* @return mixed
|
||||
* @throws DatabaseException
|
||||
* @internal param true $bool returns the generated SQL, false executes the query.
|
||||
*/
|
||||
public function replace(array $set = null)
|
||||
{
|
||||
if ($set !== null)
|
||||
{
|
||||
$this->set($set);
|
||||
}
|
||||
|
||||
if (! $this->QBSet)
|
||||
{
|
||||
if (CI_DEBUG)
|
||||
{
|
||||
throw new DatabaseException('You must use the "set" method to update an entry.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$table = $this->QBFrom[0];
|
||||
|
||||
$set = $this->binds;
|
||||
|
||||
// We need to grab out the actual values from
|
||||
// the way binds are stored with escape flag.
|
||||
array_walk($set, function (&$item) {
|
||||
$item = $item[0];
|
||||
});
|
||||
|
||||
$keys = array_keys($set);
|
||||
$values = array_values($set);
|
||||
|
||||
$builder = $this->db->table($table);
|
||||
$exists = $builder->where("$keys[0] = $values[0]", null, false)->get()->getFirstRow();
|
||||
|
||||
if (empty($exists))
|
||||
{
|
||||
$result = $builder->insert($set);
|
||||
}
|
||||
else
|
||||
{
|
||||
array_pop($set);
|
||||
$result = $builder->update($set, "$keys[0] = $values[0]");
|
||||
}
|
||||
|
||||
unset($builder);
|
||||
$this->resetWrite();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*
|
||||
* Compiles a delete string and runs the query
|
||||
*
|
||||
* @param mixed $where
|
||||
* @param integer $limit
|
||||
* @param boolean $reset_data
|
||||
*
|
||||
* @return mixed
|
||||
* @throws DatabaseException
|
||||
* @internal param the $mixed where clause
|
||||
* @internal param the $mixed limit clause
|
||||
* @internal param $bool
|
||||
*/
|
||||
public function delete($where = '', int $limit = null, bool $reset_data = true)
|
||||
{
|
||||
if (! empty($limit) || ! empty($this->QBLimit))
|
||||
{
|
||||
throw new DatabaseException('PostgreSQL does not allow LIMITs on DELETE queries.');
|
||||
}
|
||||
|
||||
return parent::delete($where, $limit, $reset_data);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* LIMIT string
|
||||
*
|
||||
* Generates a platform-specific LIMIT clause.
|
||||
*
|
||||
* @param string $sql SQL Query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _limit(string $sql): string
|
||||
{
|
||||
return $sql . ' LIMIT ' . $this->QBLimit . ($this->QBOffset ? " OFFSET {$this->QBOffset}" : '');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Update statement
|
||||
*
|
||||
* Generates a platform-specific update string from the supplied data
|
||||
*
|
||||
* @param string $table
|
||||
* @param array $values
|
||||
*
|
||||
* @return string
|
||||
* @throws DatabaseException
|
||||
* @internal param the $string table name
|
||||
* @internal param the $array update data
|
||||
*/
|
||||
protected function _update(string $table, array $values): string
|
||||
{
|
||||
if (! empty($this->QBLimit))
|
||||
{
|
||||
throw new DatabaseException('Postgres does not support LIMITs with UPDATE queries.');
|
||||
}
|
||||
|
||||
$this->QBOrderBy = [];
|
||||
return parent::_update($table, $values);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Update_Batch statement
|
||||
*
|
||||
* Generates a platform-specific batch update string from the supplied data
|
||||
*
|
||||
* @param string $table Table name
|
||||
* @param array $values Update data
|
||||
* @param string $index WHERE key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _updateBatch(string $table, array $values, string $index): string
|
||||
{
|
||||
$ids = [];
|
||||
foreach ($values as $key => $val)
|
||||
{
|
||||
$ids[] = $val[$index];
|
||||
|
||||
foreach (array_keys($val) as $field)
|
||||
{
|
||||
if ($field !== $index)
|
||||
{
|
||||
$final[$field][] = "WHEN {$val[$index]} THEN {$val[$field]}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cases = '';
|
||||
foreach ($final as $k => $v)
|
||||
{
|
||||
$cases .= "{$k} = (CASE {$index}\n"
|
||||
. implode("\n", $v)
|
||||
. "\nELSE {$k} END), ";
|
||||
}
|
||||
|
||||
$this->where("{$index} IN(" . implode(',', $ids) . ')', null, false);
|
||||
|
||||
return "UPDATE {$table} SET " . substr($cases, 0, -2) . $this->compileWhereHaving('QBWhere');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Delete statement
|
||||
*
|
||||
* Generates a platform-specific delete string from the supplied data
|
||||
*
|
||||
* @param string $table The table name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _delete(string $table): string
|
||||
{
|
||||
$this->QBLimit = false;
|
||||
return parent::_delete($table);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Truncate statement
|
||||
*
|
||||
* Generates a platform-specific truncate string from the supplied data
|
||||
*
|
||||
* If the database does not support the truncate() command,
|
||||
* then this method maps to 'DELETE FROM table'
|
||||
*
|
||||
* @param string $table The table name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _truncate(string $table): string
|
||||
{
|
||||
return 'TRUNCATE ' . $table . ' RESTART IDENTITY';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform independent LIKE statement builder.
|
||||
*
|
||||
* In PostgreSQL, the ILIKE operator will perform case insensitive
|
||||
* searches according to the current locale.
|
||||
*
|
||||
* @see https://www.postgresql.org/docs/9.2/static/functions-matching.html
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param string $column
|
||||
* @param string $not
|
||||
* @param string $bind
|
||||
* @param boolean $insensitiveSearch
|
||||
*
|
||||
* @return string $like_statement
|
||||
*/
|
||||
public function _like_statement(string $prefix = null, string $column, string $not = null, string $bind, bool $insensitiveSearch = false): string
|
||||
{
|
||||
$op = $insensitiveSearch === true ? 'ILIKE' : 'LIKE';
|
||||
|
||||
return "{$prefix} {$column} {$not} {$op} :{$bind}:";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
614
system/Database/Postgre/Connection.php
Normal file
614
system/Database/Postgre/Connection.php
Normal file
@@ -0,0 +1,614 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\Postgre;
|
||||
|
||||
use CodeIgniter\Database\BaseConnection;
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Connection for Postgre
|
||||
*/
|
||||
class Connection extends BaseConnection implements ConnectionInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Database driver
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $DBDriver = 'postgre';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Database schema
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $schema = 'public';
|
||||
|
||||
/**
|
||||
* Identifier escape character
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $escapeChar = '"';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Connect to the database.
|
||||
*
|
||||
* @param boolean $persistent
|
||||
* @return mixed
|
||||
*/
|
||||
public function connect(bool $persistent = false)
|
||||
{
|
||||
if (empty($this->DSN))
|
||||
{
|
||||
$this->buildDSN();
|
||||
}
|
||||
|
||||
// Strip pgsql if exists
|
||||
if (mb_strpos($this->DSN, 'pgsql:') === 0)
|
||||
{
|
||||
$this->DSN = mb_substr($this->DSN, 6);
|
||||
}
|
||||
|
||||
// Convert semicolons to spaces.
|
||||
$this->DSN = str_replace(';', ' ', $this->DSN);
|
||||
|
||||
$this->connID = $persistent === true ? pg_pconnect($this->DSN) : pg_connect($this->DSN);
|
||||
|
||||
if ($this->connID !== false)
|
||||
{
|
||||
if ($persistent === true && pg_connection_status($this->connID) === PGSQL_CONNECTION_BAD && pg_ping($this->connID) === false
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
empty($this->schema) || $this->simpleQuery("SET search_path TO {$this->schema},public");
|
||||
|
||||
if ($this->setClientEncoding($this->charset) === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->connID;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Keep or establish the connection if no queries have been sent for
|
||||
* a length of time exceeding the server's idle timeout.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reconnect()
|
||||
{
|
||||
if (pg_ping($this->connID) === false)
|
||||
{
|
||||
$this->connID = false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Close the database connection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _close()
|
||||
{
|
||||
pg_close($this->connID);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Select a specific database table to use.
|
||||
*
|
||||
* @param string $databaseName
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function setDatabase(string $databaseName): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a string containing the version of the database being used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion(): string
|
||||
{
|
||||
if (isset($this->dataCache['version']))
|
||||
{
|
||||
return $this->dataCache['version'];
|
||||
}
|
||||
|
||||
if (! $this->connID || ( $pgVersion = pg_version($this->connID)) === false)
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return isset($pgVersion['server']) ? $this->dataCache['version'] = $pgVersion['server'] : false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Executes the query against the database.
|
||||
*
|
||||
* @param string $sql
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public function execute(string $sql)
|
||||
{
|
||||
return pg_query($this->connID, $sql);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the total number of rows affected by this query.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function affectedRows(): int
|
||||
{
|
||||
return pg_affected_rows($this->resultID);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* "Smart" Escape String
|
||||
*
|
||||
* Escapes data based on type
|
||||
*
|
||||
* @param mixed $str
|
||||
* @return mixed
|
||||
*/
|
||||
public function escape($str)
|
||||
{
|
||||
if (! $this->connID)
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
if (is_string($str) || ( is_object($str) && method_exists($str, '__toString')))
|
||||
{
|
||||
return pg_escape_literal($this->connID, $str);
|
||||
}
|
||||
elseif (is_bool($str))
|
||||
{
|
||||
return $str ? 'TRUE' : 'FALSE';
|
||||
}
|
||||
|
||||
return parent::escape($str);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform-dependant string escape
|
||||
*
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
protected function _escapeString(string $str): string
|
||||
{
|
||||
if (! $this->connID)
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return pg_escape_string($this->connID, $str);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates the SQL for listing tables in a platform-dependent manner.
|
||||
*
|
||||
* @param boolean $prefixLimit
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _listTables(bool $prefixLimit = false): string
|
||||
{
|
||||
$sql = 'SELECT "table_name" FROM "information_schema"."tables" WHERE "table_schema" = \'' . $this->schema . "'";
|
||||
|
||||
if ($prefixLimit !== false && $this->DBPrefix !== '')
|
||||
{
|
||||
return $sql . ' AND "table_name" LIKE \''
|
||||
. $this->escapeLikeString($this->DBPrefix) . "%' "
|
||||
. sprintf($this->likeEscapeStr, $this->likeEscapeChar);
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates a platform-specific query string so that the column names can be fetched.
|
||||
*
|
||||
* @param string $table
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _listColumns(string $table = ''): string
|
||||
{
|
||||
return 'SELECT "column_name"
|
||||
FROM "information_schema"."columns"
|
||||
WHERE LOWER("table_name") = '
|
||||
. $this->escape($this->DBPrefix . strtolower($table));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with field data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _fieldData(string $table): array
|
||||
{
|
||||
$sql = 'SELECT "column_name", "data_type", "character_maximum_length", "numeric_precision", "column_default"
|
||||
FROM "information_schema"."columns"
|
||||
WHERE LOWER("table_name") = '
|
||||
. $this->escape(strtolower($table));
|
||||
|
||||
if (($query = $this->query($sql)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetFieldData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
$retVal = [];
|
||||
for ($i = 0, $c = count($query); $i < $c; $i ++)
|
||||
{
|
||||
$retVal[$i] = new \stdClass();
|
||||
$retVal[$i]->name = $query[$i]->column_name;
|
||||
$retVal[$i]->type = $query[$i]->data_type;
|
||||
$retVal[$i]->default = $query[$i]->column_default;
|
||||
$retVal[$i]->max_length = $query[$i]->character_maximum_length > 0 ? $query[$i]->character_maximum_length : $query[$i]->numeric_precision;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with index data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _indexData(string $table): array
|
||||
{
|
||||
$sql = 'SELECT "indexname", "indexdef"
|
||||
FROM "pg_indexes"
|
||||
WHERE LOWER("tablename") = ' . $this->escape(strtolower($table)) . '
|
||||
AND "schemaname" = ' . $this->escape('public');
|
||||
|
||||
if (($query = $this->query($sql)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetIndexData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
$retVal = [];
|
||||
foreach ($query as $row)
|
||||
{
|
||||
$obj = new \stdClass();
|
||||
$obj->name = $row->indexname;
|
||||
$_fields = explode(',', preg_replace('/^.*\((.+?)\)$/', '$1', trim($row->indexdef)));
|
||||
$obj->fields = array_map(function ($v) {
|
||||
return trim($v);
|
||||
}, $_fields);
|
||||
|
||||
if (strpos($row->indexdef, 'CREATE UNIQUE INDEX pk') === 0)
|
||||
{
|
||||
$obj->type = 'PRIMARY';
|
||||
}
|
||||
else
|
||||
{
|
||||
$obj->type = (strpos($row->indexdef, 'CREATE UNIQUE') === 0) ? 'UNIQUE' : 'INDEX';
|
||||
}
|
||||
|
||||
$retVal[$obj->name] = $obj;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with Foreign key data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _foreignKeyData(string $table): array
|
||||
{
|
||||
$sql = 'SELECT
|
||||
tc.constraint_name, tc.table_name, kcu.column_name,
|
||||
ccu.table_name AS foreign_table_name,
|
||||
ccu.column_name AS foreign_column_name
|
||||
FROM information_schema.table_constraints AS tc
|
||||
JOIN information_schema.key_column_usage AS kcu
|
||||
ON tc.constraint_name = kcu.constraint_name
|
||||
JOIN information_schema.constraint_column_usage AS ccu
|
||||
ON ccu.constraint_name = tc.constraint_name
|
||||
WHERE constraint_type = ' . $this->escape('FOREIGN KEY') . ' AND
|
||||
tc.table_name = ' . $this->escape($table);
|
||||
|
||||
if (($query = $this->query($sql)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetForeignKeyData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
$retVal = [];
|
||||
foreach ($query as $row)
|
||||
{
|
||||
$obj = new \stdClass();
|
||||
$obj->constraint_name = $row->constraint_name;
|
||||
$obj->table_name = $row->table_name;
|
||||
$obj->column_name = $row->column_name;
|
||||
$obj->foreign_table_name = $row->foreign_table_name;
|
||||
$obj->foreign_column_name = $row->foreign_column_name;
|
||||
$retVal[] = $obj;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns platform-specific SQL to disable foreign key checks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _disableForeignKeyChecks()
|
||||
{
|
||||
return 'SET CONSTRAINTS ALL DEFERRED';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns platform-specific SQL to enable foreign key checks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _enableForeignKeyChecks()
|
||||
{
|
||||
return 'SET CONSTRAINTS ALL IMMEDIATE;';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the last error code and message.
|
||||
*
|
||||
* Must return an array with keys 'code' and 'message':
|
||||
*
|
||||
* return ['code' => null, 'message' => null);
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function error(): array
|
||||
{
|
||||
return [
|
||||
'code' => '',
|
||||
'message' => pg_last_error($this->connID),
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Insert ID
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function insertID(): int
|
||||
{
|
||||
$v = pg_version($this->connID);
|
||||
// 'server' key is only available since PostgreSQL 7.4
|
||||
$v = $v['server'] ?? 0;
|
||||
|
||||
$table = func_num_args() > 0 ? func_get_arg(0) : null;
|
||||
$column = func_num_args() > 1 ? func_get_arg(1) : null;
|
||||
|
||||
if ($table === null && $v >= '8.1')
|
||||
{
|
||||
$sql = 'SELECT LASTVAL() AS ins_id';
|
||||
}
|
||||
elseif ($table !== null)
|
||||
{
|
||||
if ($column !== null && $v >= '8.0')
|
||||
{
|
||||
$sql = "SELECT pg_get_serial_sequence('{$table}', '{$column}') AS seq";
|
||||
$query = $this->query($sql);
|
||||
$query = $query->getRow();
|
||||
$seq = $query->seq;
|
||||
}
|
||||
else
|
||||
{
|
||||
// seq_name passed in table parameter
|
||||
$seq = $table;
|
||||
}
|
||||
|
||||
$sql = "SELECT CURRVAL('{$seq}') AS ins_id";
|
||||
}
|
||||
else
|
||||
{
|
||||
return pg_last_oid($this->resultID);
|
||||
}
|
||||
|
||||
$query = $this->query($sql);
|
||||
$query = $query->getRow();
|
||||
return (int) $query->ins_id;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Build a DSN from the provided parameters
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function buildDSN()
|
||||
{
|
||||
$this->DSN === '' || $this->DSN = '';
|
||||
|
||||
// If UNIX sockets are used, we shouldn't set a port
|
||||
if (strpos($this->hostname, '/') !== false)
|
||||
{
|
||||
$this->port = '';
|
||||
}
|
||||
|
||||
$this->hostname === '' || $this->DSN = "host={$this->hostname} ";
|
||||
|
||||
if (! empty($this->port) && ctype_digit($this->port))
|
||||
{
|
||||
$this->DSN .= "port={$this->port} ";
|
||||
}
|
||||
|
||||
if ($this->username !== '')
|
||||
{
|
||||
$this->DSN .= "user={$this->username} ";
|
||||
|
||||
// An empty password is valid!
|
||||
// password must be set to null to ignore it.
|
||||
|
||||
$this->password === null || $this->DSN .= "password='{$this->password}' ";
|
||||
}
|
||||
|
||||
$this->database === '' || $this->DSN .= "dbname={$this->database} ";
|
||||
|
||||
// We don't have these options as elements in our standard configuration
|
||||
// array, but they might be set by parse_url() if the configuration was
|
||||
// provided via string> Example:
|
||||
//
|
||||
// postgre://username:password@localhost:5432/database?connect_timeout=5&sslmode=1
|
||||
foreach (['connect_timeout', 'options', 'sslmode', 'service'] as $key)
|
||||
{
|
||||
if (isset($this->{$key}) && is_string($this->{$key}) && $this->{$key} !== '')
|
||||
{
|
||||
$this->DSN .= "{$key}='{$this->{$key}}' ";
|
||||
}
|
||||
}
|
||||
|
||||
$this->DSN = rtrim($this->DSN);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Set client encoding
|
||||
*
|
||||
* @param string $charset The client encoding to which the data will be converted.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function setClientEncoding(string $charset): bool
|
||||
{
|
||||
return pg_set_client_encoding($this->connID, $charset) === 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Begin Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transBegin(): bool
|
||||
{
|
||||
return (bool) pg_query($this->connID, 'BEGIN');
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Commit Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transCommit(): bool
|
||||
{
|
||||
return (bool) pg_query($this->connID, 'COMMIT');
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Rollback Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transRollback(): bool
|
||||
{
|
||||
return (bool) pg_query($this->connID, 'ROLLBACK');
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
}
|
||||
261
system/Database/Postgre/Forge.php
Normal file
261
system/Database/Postgre/Forge.php
Normal file
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\Postgre;
|
||||
|
||||
/**
|
||||
* Forge for Postgre
|
||||
*/
|
||||
class Forge extends \CodeIgniter\Database\Forge
|
||||
{
|
||||
|
||||
/**
|
||||
* CHECK DATABASE EXIST statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $checkDatabaseExistStr = 'SELECT 1 FROM pg_database WHERE datname = ?';
|
||||
|
||||
/**
|
||||
* DROP CONSTRAINT statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $dropConstraintStr = 'ALTER TABLE %s DROP CONSTRAINT %s';
|
||||
|
||||
/**
|
||||
* UNSIGNED support
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_unsigned = [
|
||||
'INT2' => 'INTEGER',
|
||||
'SMALLINT' => 'INTEGER',
|
||||
'INT' => 'BIGINT',
|
||||
'INT4' => 'BIGINT',
|
||||
'INTEGER' => 'BIGINT',
|
||||
'INT8' => 'NUMERIC',
|
||||
'BIGINT' => 'NUMERIC',
|
||||
'REAL' => 'DOUBLE PRECISION',
|
||||
'FLOAT' => 'DOUBLE PRECISION',
|
||||
];
|
||||
|
||||
/**
|
||||
* NULL value representation in CREATE/ALTER TABLE statements
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_null = 'NULL';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* CREATE TABLE attributes
|
||||
*
|
||||
* @param array $attributes Associative array of table attributes
|
||||
* @return string
|
||||
*/
|
||||
protected function _createTableAttributes(array $attributes): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ALTER TABLE
|
||||
*
|
||||
* @param string $alter_type ALTER type
|
||||
* @param string $table Table name
|
||||
* @param mixed $field Column definition
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
protected function _alterTable(string $alter_type, string $table, $field)
|
||||
{
|
||||
if (in_array($alter_type, ['DROP', 'ADD'], true))
|
||||
{
|
||||
return parent::_alterTable($alter_type, $table, $field);
|
||||
}
|
||||
|
||||
$sql = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table);
|
||||
$sqls = [];
|
||||
foreach ($field as $data)
|
||||
{
|
||||
if ($data['_literal'] !== false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version_compare($this->db->getVersion(), '8', '>=') && isset($data['type']))
|
||||
{
|
||||
$sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($data['name'])
|
||||
. " TYPE {$data['type']}{$data['length']}";
|
||||
}
|
||||
|
||||
if (! empty($data['default']))
|
||||
{
|
||||
$sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($data['name'])
|
||||
. " SET DEFAULT {$data['default']}";
|
||||
}
|
||||
|
||||
if (isset($data['null']))
|
||||
{
|
||||
$sqls[] = $sql . ' ALTER COLUMN ' . $this->db->escapeIdentifiers($data['name'])
|
||||
. ($data['null'] === true ? ' DROP' : ' SET') . ' NOT NULL';
|
||||
}
|
||||
|
||||
if (! empty($data['new_name']))
|
||||
{
|
||||
$sqls[] = $sql . ' RENAME COLUMN ' . $this->db->escapeIdentifiers($data['name'])
|
||||
. ' TO ' . $this->db->escapeIdentifiers($data['new_name']);
|
||||
}
|
||||
|
||||
if (! empty($data['comment']))
|
||||
{
|
||||
$sqls[] = 'COMMENT ON COLUMN' . $this->db->escapeIdentifiers($table)
|
||||
. '.' . $this->db->escapeIdentifiers($data['name'])
|
||||
. " IS {$data['comment']}";
|
||||
}
|
||||
}
|
||||
|
||||
return $sqls;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process column
|
||||
*
|
||||
* @param array $field
|
||||
* @return string
|
||||
*/
|
||||
protected function _processColumn(array $field): string
|
||||
{
|
||||
return $this->db->escapeIdentifiers($field['name'])
|
||||
. ' ' . $field['type'] . $field['length']
|
||||
. $field['default']
|
||||
. $field['null']
|
||||
. $field['auto_increment']
|
||||
. $field['unique'];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Field attribute TYPE
|
||||
*
|
||||
* Performs a data type mapping between different databases.
|
||||
*
|
||||
* @param array &$attributes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _attributeType(array &$attributes)
|
||||
{
|
||||
// Reset field lengths for data types that don't support it
|
||||
if (isset($attributes['CONSTRAINT']) && stripos($attributes['TYPE'], 'int') !== false)
|
||||
{
|
||||
$attributes['CONSTRAINT'] = null;
|
||||
}
|
||||
|
||||
switch (strtoupper($attributes['TYPE']))
|
||||
{
|
||||
case 'TINYINT':
|
||||
$attributes['TYPE'] = 'SMALLINT';
|
||||
$attributes['UNSIGNED'] = false;
|
||||
break;
|
||||
case 'MEDIUMINT':
|
||||
$attributes['TYPE'] = 'INTEGER';
|
||||
$attributes['UNSIGNED'] = false;
|
||||
break;
|
||||
case 'DATETIME':
|
||||
$attributes['TYPE'] = 'TIMESTAMP';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Field attribute AUTO_INCREMENT
|
||||
*
|
||||
* @param array &$attributes
|
||||
* @param array &$field
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _attributeAutoIncrement(array &$attributes, array &$field)
|
||||
{
|
||||
if (! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === true)
|
||||
{
|
||||
$field['type'] = $field['type'] === 'NUMERIC' ? 'BIGSERIAL' : 'SERIAL';
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Drop Table
|
||||
*
|
||||
* Generates a platform-specific DROP TABLE string
|
||||
*
|
||||
* @param string $table Table name
|
||||
* @param boolean $if_exists Whether to add an IF EXISTS condition
|
||||
* @param boolean $cascade
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _dropTable(string $table, bool $if_exists, bool $cascade): string
|
||||
{
|
||||
$sql = parent::_dropTable($table, $if_exists, $cascade);
|
||||
|
||||
if ($cascade === true)
|
||||
{
|
||||
$sql .= ' CASCADE';
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
}
|
||||
160
system/Database/Postgre/PreparedQuery.php
Normal file
160
system/Database/Postgre/PreparedQuery.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\Postgre;
|
||||
|
||||
use CodeIgniter\Database\PreparedQueryInterface;
|
||||
use CodeIgniter\Database\BasePreparedQuery;
|
||||
|
||||
/**
|
||||
* Prepared query for Postgre
|
||||
*/
|
||||
class PreparedQuery extends BasePreparedQuery implements PreparedQueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Stores the name this query can be
|
||||
* used under by postgres. Only used internally.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The result resource from a successful
|
||||
* pg_exec. Or false.
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepares the query against the database, and saves the connection
|
||||
* info necessary to execute the query later.
|
||||
*
|
||||
* NOTE: This version is based on SQL code. Child classes should
|
||||
* override this method.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $options Passed to the connection's prepare statement.
|
||||
* Unused in the MySQLi driver.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _prepare(string $sql, array $options = [])
|
||||
{
|
||||
$this->name = random_int(1, 10000000000000000);
|
||||
|
||||
$sql = $this->parameterize($sql);
|
||||
|
||||
// Update the query object since the parameters are slightly different
|
||||
// than what was put in.
|
||||
$this->query->setQuery($sql);
|
||||
|
||||
if (! $this->statement = pg_prepare($this->db->connID, $this->name, $sql))
|
||||
{
|
||||
$this->errorCode = 0;
|
||||
$this->errorString = pg_last_error($this->db->connID);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes a new set of data and runs it against the currently
|
||||
* prepared query. Upon success, will return a Results object.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function _execute(array $data): bool
|
||||
{
|
||||
if (is_null($this->statement))
|
||||
{
|
||||
throw new \BadMethodCallException('You must call prepare before trying to execute a prepared statement.');
|
||||
}
|
||||
|
||||
$this->result = pg_execute($this->db->connID, $this->name, $data);
|
||||
|
||||
return (bool) $this->result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result object for the prepared query.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Replaces the ? placeholders with $1, $2, etc parameters for use
|
||||
* within the prepared query.
|
||||
*
|
||||
* @param string $sql
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function parameterize(string $sql): string
|
||||
{
|
||||
// Track our current value
|
||||
$count = 0;
|
||||
|
||||
$sql = preg_replace_callback('/\?/', function ($matches) use (&$count) {
|
||||
$count ++;
|
||||
return "\${$count}";
|
||||
}, $sql);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
171
system/Database/Postgre/Result.php
Normal file
171
system/Database/Postgre/Result.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\Postgre;
|
||||
|
||||
use CodeIgniter\Database\BaseResult;
|
||||
use CodeIgniter\Database\ResultInterface;
|
||||
use CodeIgniter\Entity;
|
||||
|
||||
/**
|
||||
* Result for Postgre
|
||||
*/
|
||||
class Result extends BaseResult implements ResultInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets the number of fields in the result set.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getFieldCount(): int
|
||||
{
|
||||
return pg_num_fields($this->resultID);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of column names in the result set.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldNames(): array
|
||||
{
|
||||
$fieldNames = [];
|
||||
for ($i = 0, $c = $this->getFieldCount(); $i < $c; $i ++)
|
||||
{
|
||||
$fieldNames[] = pg_field_name($this->resultID, $i);
|
||||
}
|
||||
|
||||
return $fieldNames;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of objects representing field meta-data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldData(): array
|
||||
{
|
||||
$retVal = [];
|
||||
|
||||
for ($i = 0, $c = $this->getFieldCount(); $i < $c; $i ++)
|
||||
{
|
||||
$retVal[$i] = new \stdClass();
|
||||
$retVal[$i]->name = pg_field_name($this->resultID, $i);
|
||||
$retVal[$i]->type = pg_field_type($this->resultID, $i);
|
||||
$retVal[$i]->max_length = pg_field_size($this->resultID, $i);
|
||||
// $retVal[$i]->primary_key = (int)($fieldData[$i]->flags & 2);
|
||||
// $retVal[$i]->default = $fieldData[$i]->def;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Frees the current result.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function freeResult()
|
||||
{
|
||||
if (is_resource($this->resultID))
|
||||
{
|
||||
pg_free_result($this->resultID);
|
||||
$this->resultID = false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Moves the internal pointer to the desired offset. This is called
|
||||
* internally before fetching results to make sure the result set
|
||||
* starts at zero.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function dataSeek(int $n = 0)
|
||||
{
|
||||
return pg_result_seek($this->resultID, $n);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an array.
|
||||
*
|
||||
* Overridden by driver classes.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fetchAssoc()
|
||||
{
|
||||
return pg_fetch_assoc($this->resultID);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an object.
|
||||
*
|
||||
* Overridden by child classes.
|
||||
*
|
||||
* @param string $className
|
||||
*
|
||||
* @return object|boolean|Entity
|
||||
*/
|
||||
protected function fetchObject(string $className = 'stdClass')
|
||||
{
|
||||
if (is_subclass_of($className, Entity::class))
|
||||
{
|
||||
return empty($data = $this->fetchAssoc()) ? false : (new $className())->setAttributes($data);
|
||||
}
|
||||
return pg_fetch_object($this->resultID, null, $className);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
79
system/Database/Postgre/Utils.php
Normal file
79
system/Database/Postgre/Utils.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\Postgre;
|
||||
|
||||
use CodeIgniter\Database\BaseUtils;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Utils for Postgre
|
||||
*/
|
||||
class Utils extends BaseUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* List databases statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $listDatabases = 'SELECT datname FROM pg_database';
|
||||
|
||||
/**
|
||||
* OPTIMIZE TABLE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $optimizeTable = 'REINDEX TABLE %s';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform dependent version of the backup function.
|
||||
*
|
||||
* @param array|null $prefs
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _backup(array $prefs = null)
|
||||
{
|
||||
throw new DatabaseException('Unsupported feature of the database platform you are using.');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
106
system/Database/PreparedQueryInterface.php
Normal file
106
system/Database/PreparedQueryInterface.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database;
|
||||
|
||||
/**
|
||||
* Prepared query interface
|
||||
*/
|
||||
interface PreparedQueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Takes a new set of data and runs it against the currently
|
||||
* prepared query. Upon success, will return a Results object.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return ResultInterface
|
||||
*/
|
||||
public function execute(...$data);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepares the query against the database, and saves the connection
|
||||
* info necessary to execute the query later.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $options Passed to the connection's prepare statement.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function prepare(string $sql, array $options = []);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Explicity closes the statement.
|
||||
*/
|
||||
public function close();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the SQL that has been prepared.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryString(): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error code created while executing this statement.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getErrorCode(): int;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error message created while executing this statement.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMessage(): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
506
system/Database/Query.php
Normal file
506
system/Database/Query.php
Normal file
@@ -0,0 +1,506 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database;
|
||||
|
||||
/**
|
||||
* Query builder
|
||||
*
|
||||
* @package CodeIgniter\Database
|
||||
*/
|
||||
class Query implements QueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* The query string, as provided by the user.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $originalQueryString;
|
||||
|
||||
/**
|
||||
* The final query string after binding, etc.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $finalQueryString;
|
||||
|
||||
/**
|
||||
* The binds and their values used for binding.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $binds = [];
|
||||
|
||||
/**
|
||||
* Bind marker
|
||||
*
|
||||
* Character used to identify values in a prepared statement.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bindMarker = '?';
|
||||
|
||||
/**
|
||||
* The start time in seconds with microseconds
|
||||
* for when this query was executed.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $startTime;
|
||||
|
||||
/**
|
||||
* The end time in seconds with microseconds
|
||||
* for when this query was executed.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $endTime;
|
||||
|
||||
/**
|
||||
* The error code, if any.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $errorCode;
|
||||
|
||||
/**
|
||||
* The error message, if any.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $errorString;
|
||||
|
||||
/**
|
||||
* Pointer to database connection.
|
||||
* Mainly for escaping features.
|
||||
*
|
||||
* @var BaseConnection
|
||||
*/
|
||||
public $db;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* BaseQuery constructor.
|
||||
*
|
||||
* @param $db ConnectionInterface
|
||||
*/
|
||||
public function __construct(&$db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sets the raw query string to use for this statement.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param mixed $binds
|
||||
* @param boolean $setEscape
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setQuery(string $sql, $binds = null, bool $setEscape = true)
|
||||
{
|
||||
$this->originalQueryString = $sql;
|
||||
|
||||
if (! is_null($binds))
|
||||
{
|
||||
if (! is_array($binds))
|
||||
{
|
||||
$binds = [$binds];
|
||||
}
|
||||
|
||||
if ($setEscape)
|
||||
{
|
||||
array_walk($binds, function (&$item) {
|
||||
$item = [
|
||||
$item,
|
||||
true,
|
||||
];
|
||||
});
|
||||
}
|
||||
$this->binds = $binds;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will store the variables to bind into the query later.
|
||||
*
|
||||
* @param array $binds
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setBinds(array $binds)
|
||||
{
|
||||
$this->binds = $binds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the final, processed query string after binding, etal
|
||||
* has been performed.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQuery(): string
|
||||
{
|
||||
if (empty($this->finalQueryString))
|
||||
{
|
||||
$this->finalQueryString = $this->originalQueryString;
|
||||
}
|
||||
|
||||
$this->compileBinds();
|
||||
|
||||
return $this->finalQueryString;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Records the execution time of the statement using microtime(true)
|
||||
* for it's start and end values. If no end value is present, will
|
||||
* use the current time to determine total duration.
|
||||
*
|
||||
* @param float $start
|
||||
* @param float $end
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDuration(float $start, float $end = null)
|
||||
{
|
||||
$this->startTime = $start;
|
||||
|
||||
if (is_null($end))
|
||||
{
|
||||
$end = microtime(true);
|
||||
}
|
||||
|
||||
$this->endTime = $end;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the start time in seconds with microseconds.
|
||||
*
|
||||
* @param boolean $returnRaw
|
||||
* @param integer $decimals
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStartTime(bool $returnRaw = false, int $decimals = 6): string
|
||||
{
|
||||
if ($returnRaw)
|
||||
{
|
||||
return $this->startTime;
|
||||
}
|
||||
|
||||
return number_format($this->startTime, $decimals);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Returns the duration of this query during execution, or null if
|
||||
* the query has not been executed yet.
|
||||
*
|
||||
* @param integer $decimals The accuracy of the returned time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDuration(int $decimals = 6): string
|
||||
{
|
||||
return number_format(($this->endTime - $this->startTime), $decimals);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Stores the error description that happened for this query.
|
||||
*
|
||||
* @param integer $code
|
||||
* @param string $error
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setError(int $code, string $error)
|
||||
{
|
||||
$this->errorCode = $code;
|
||||
$this->errorString = $error;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Reports whether this statement created an error not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasError(): bool
|
||||
{
|
||||
return ! empty($this->errorString);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error code created while executing this statement.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getErrorCode(): int
|
||||
{
|
||||
return $this->errorCode;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error message created while executing this statement.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMessage(): string
|
||||
{
|
||||
return $this->errorString;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the statement is a write-type query or not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isWriteType(): bool
|
||||
{
|
||||
return (bool) preg_match(
|
||||
'/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX)\s/i', $this->originalQueryString);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Swaps out one table prefix for a new one.
|
||||
*
|
||||
* @param string $orig
|
||||
* @param string $swap
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function swapPrefix(string $orig, string $swap)
|
||||
{
|
||||
$sql = empty($this->finalQueryString) ? $this->originalQueryString : $this->finalQueryString;
|
||||
|
||||
$this->finalQueryString = preg_replace('/(\W)' . $orig . '(\S+?)/', '\\1' . $swap . '\\2', $sql);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the original SQL that was passed into the system.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOriginalQuery(): string
|
||||
{
|
||||
return $this->originalQueryString;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Escapes and inserts any binds into the finalQueryString object.
|
||||
*
|
||||
* @return null|void
|
||||
*/
|
||||
protected function compileBinds()
|
||||
{
|
||||
$sql = $this->finalQueryString;
|
||||
|
||||
$hasNamedBinds = strpos($sql, ':') !== false;
|
||||
|
||||
if (empty($this->binds) || empty($this->bindMarker) ||
|
||||
(strpos($sql, $this->bindMarker) === false &&
|
||||
$hasNamedBinds === false)
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (! is_array($this->binds))
|
||||
{
|
||||
$binds = [$this->binds];
|
||||
$bindCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$binds = $this->binds;
|
||||
$bindCount = count($binds);
|
||||
}
|
||||
|
||||
// Reverse the binds so that duplicate named binds
|
||||
// will be processed prior to the original binds.
|
||||
if (! is_numeric(key(array_slice($binds, 0, 1))))
|
||||
{
|
||||
$binds = array_reverse($binds);
|
||||
}
|
||||
|
||||
// We'll need marker length later
|
||||
$ml = strlen($this->bindMarker);
|
||||
|
||||
if ($hasNamedBinds)
|
||||
{
|
||||
$sql = $this->matchNamedBinds($sql, $binds);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = $this->matchSimpleBinds($sql, $binds, $bindCount, $ml);
|
||||
}
|
||||
|
||||
$this->finalQueryString = $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Match bindings
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $binds
|
||||
* @return string
|
||||
*/
|
||||
protected function matchNamedBinds(string $sql, array $binds): string
|
||||
{
|
||||
$replacers = [];
|
||||
|
||||
foreach ($binds as $placeholder => $value)
|
||||
{
|
||||
// $value[1] contains the boolean whether should be escaped or not
|
||||
$escapedValue = $value[1] ? $this->db->escape($value[0]) : $value[0];
|
||||
|
||||
// In order to correctly handle backlashes in saved strings
|
||||
// we will need to preg_quote, so remove the wrapping escape characters
|
||||
// otherwise it will get escaped.
|
||||
if (is_array($value[0]))
|
||||
{
|
||||
$escapedValue = '(' . implode(',', $escapedValue) . ')';
|
||||
}
|
||||
|
||||
$replacers[":{$placeholder}:"] = $escapedValue;
|
||||
}
|
||||
|
||||
$sql = strtr($sql, $replacers);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Match bindings
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $binds
|
||||
* @param integer $bindCount
|
||||
* @param integer $ml
|
||||
* @return string
|
||||
*/
|
||||
protected function matchSimpleBinds(string $sql, array $binds, int $bindCount, int $ml): string
|
||||
{
|
||||
// Make sure not to replace a chunk inside a string that happens to match the bind marker
|
||||
if ($c = preg_match_all("/'[^']*'/i", $sql, $matches))
|
||||
{
|
||||
$c = preg_match_all('/' . preg_quote($this->bindMarker, '/') . '/i', str_replace($matches[0], str_replace($this->bindMarker, str_repeat(' ', $ml), $matches[0]), $sql, $c), $matches, PREG_OFFSET_CAPTURE);
|
||||
|
||||
// Bind values' count must match the count of markers in the query
|
||||
if ($bindCount !== $c)
|
||||
{
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
// Number of binds must match bindMarkers in the string.
|
||||
else if (($c = preg_match_all('/' . preg_quote($this->bindMarker, '/') . '/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bindCount)
|
||||
{
|
||||
return $sql;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
$c--;
|
||||
$escapedValue = $binds[$c][1] ? $this->db->escape($binds[$c][0]) : $binds[$c][0];
|
||||
if (is_array($escapedValue))
|
||||
{
|
||||
$escapedValue = '(' . implode(',', $escapedValue) . ')';
|
||||
}
|
||||
$sql = substr_replace($sql, $escapedValue, $matches[0][$c][1], $ml);
|
||||
}
|
||||
while ($c !== 0);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return text representation of the query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getQuery();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
159
system/Database/QueryInterface.php
Normal file
159
system/Database/QueryInterface.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database;
|
||||
|
||||
/**
|
||||
* Interface QueryInterface
|
||||
*
|
||||
* Represents a single statement that can be executed against the database.
|
||||
* Statements are platform-specific and can handle binding of binds.
|
||||
*
|
||||
* @package CodeIgniter\Database
|
||||
*/
|
||||
interface QueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Sets the raw query string to use for this statement.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param mixed $binds
|
||||
* @param boolean $setEscape
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setQuery(string $sql, $binds = null, bool $setEscape = true);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the final, processed query string after binding, etal
|
||||
* has been performed.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getQuery();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Records the execution time of the statement using microtime(true)
|
||||
* for it's start and end values. If no end value is present, will
|
||||
* use the current time to determine total duration.
|
||||
*
|
||||
* @param float $start
|
||||
* @param float $end
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setDuration(float $start, float $end = null);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the duration of this query during execution, or null if
|
||||
* the query has not been executed yet.
|
||||
*
|
||||
* @param integer $decimals The accuracy of the returned time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDuration(int $decimals = 6): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Stores the error description that happened for this query.
|
||||
*
|
||||
* @param integer $code
|
||||
* @param string $error
|
||||
*/
|
||||
public function setError(int $code, string $error);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Reports whether this statement created an error not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasError(): bool;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error code created while executing this statement.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getErrorCode(): int;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the error message created while executing this statement.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMessage(): string;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the statement is a write-type query or not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isWriteType(): bool;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Swaps out one table prefix for a new one.
|
||||
*
|
||||
* @param string $orig
|
||||
* @param string $swap
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function swapPrefix(string $orig, string $swap);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
264
system/Database/ResultInterface.php
Normal file
264
system/Database/ResultInterface.php
Normal file
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database;
|
||||
|
||||
/**
|
||||
* Interface ResultInterface
|
||||
*/
|
||||
interface ResultInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Retrieve the results of the query. Typically an array of
|
||||
* individual data rows, which can be either an 'array', an
|
||||
* 'object', or a custom class name.
|
||||
*
|
||||
* @param string $type The row type. Either 'array', 'object', or a class name to use
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResult(string $type = 'object'): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the results as an array of custom objects.
|
||||
*
|
||||
* @param string $className The name of the class to use.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCustomResultObject(string $className);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the results as an array of arrays.
|
||||
*
|
||||
* If no results, an empty array is returned.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResultArray(): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the results as an array of objects.
|
||||
*
|
||||
* If no results, an empty array is returned.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResultObject(): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Wrapper object to return a row as either an array, an object, or
|
||||
* a custom class.
|
||||
*
|
||||
* If row doesn't exist, returns null.
|
||||
*
|
||||
* @param mixed $n The index of the results to return
|
||||
* @param string $type The type of result object. 'array', 'object' or class name.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRow($n = 0, string $type = 'object');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a row as a custom class instance.
|
||||
*
|
||||
* If row doesn't exists, returns null.
|
||||
*
|
||||
* @param integer $n
|
||||
* @param string $className
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCustomRowObject(int $n, string $className);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a single row from the results as an array.
|
||||
*
|
||||
* If row doesn't exist, returns null.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRowArray(int $n = 0);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a single row from the results as an object.
|
||||
*
|
||||
* If row doesn't exist, returns null.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRowObject(int $n = 0);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Assigns an item into a particular column slot.
|
||||
*
|
||||
* @param $key
|
||||
* @param null $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setRow($key, $value = null);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "first" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFirstRow(string $type = 'object');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "last" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLastRow(string $type = 'object');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "next" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNextRow(string $type = 'object');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the "previous" row of the current results.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPreviousRow(string $type = 'object');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an unbuffered row and move the pointer to the next row.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUnbufferedRow(string $type = 'object');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets the number of fields in the result set.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getFieldCount(): int;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of column names in the result set.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldNames(): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of objects representing field meta-data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldData(): array;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Frees the current result.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function freeResult();
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Moves the internal pointer to the desired offset. This is called
|
||||
* internally before fetching results to make sure the result set
|
||||
* starts at zero.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function dataSeek(int $n = 0);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
111
system/Database/SQLite3/Builder.php
Normal file
111
system/Database/SQLite3/Builder.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\BaseBuilder;
|
||||
|
||||
/**
|
||||
* Builder for SQLite3
|
||||
*/
|
||||
class Builder extends BaseBuilder
|
||||
{
|
||||
|
||||
/**
|
||||
* Identifier escape character
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $escapeChar = '`';
|
||||
|
||||
/**
|
||||
* Default installs of SQLite typically do not
|
||||
* support limiting delete clauses.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $canLimitDeletes = false;
|
||||
|
||||
/**
|
||||
* Default installs of SQLite do no support
|
||||
* limiting update queries in combo with WHERE.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $canLimitWhereUpdates = false;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Replace statement
|
||||
*
|
||||
* Generates a platform-specific replace string from the supplied data
|
||||
*
|
||||
* @param string $table the table name
|
||||
* @param array $keys the insert keys
|
||||
* @param array $values the insert values
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _replace(string $table, array $keys, array $values): string
|
||||
{
|
||||
return 'INSERT OR ' . parent::_replace($table, $keys, $values);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Truncate statement
|
||||
*
|
||||
* Generates a platform-specific truncate string from the supplied data
|
||||
*
|
||||
* If the database does not support the TRUNCATE statement,
|
||||
* then this method maps to 'DELETE FROM table'
|
||||
*
|
||||
* @param string $table
|
||||
* @return string
|
||||
*/
|
||||
protected function _truncate(string $table): string
|
||||
{
|
||||
return 'DELETE FROM ' . $table;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
}
|
||||
542
system/Database/SQLite3/Connection.php
Normal file
542
system/Database/SQLite3/Connection.php
Normal file
@@ -0,0 +1,542 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\BaseConnection;
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Connection for SQLite3
|
||||
*/
|
||||
class Connection extends BaseConnection implements ConnectionInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Database driver
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $DBDriver = 'SQLite3';
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ORDER BY random keyword
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_random_keyword = [
|
||||
'RANDOM()',
|
||||
];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Connect to the database.
|
||||
*
|
||||
* @param boolean $persistent
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function connect(bool $persistent = false)
|
||||
{
|
||||
if ($persistent && $this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('SQLite3 doesn\'t support persistent connections.');
|
||||
}
|
||||
try
|
||||
{
|
||||
return (! $this->password)
|
||||
? new \SQLite3($this->database)
|
||||
: new \SQLite3($this->database, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $this->password);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
throw new DatabaseException('SQLite3 error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Keep or establish the connection if no queries have been sent for
|
||||
* a length of time exceeding the server's idle timeout.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reconnect()
|
||||
{
|
||||
$this->close();
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Close the database connection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _close()
|
||||
{
|
||||
$this->connID->close();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Select a specific database table to use.
|
||||
*
|
||||
* @param string $databaseName
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function setDatabase(string $databaseName): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a string containing the version of the database being used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion(): string
|
||||
{
|
||||
if (isset($this->dataCache['version']))
|
||||
{
|
||||
return $this->dataCache['version'];
|
||||
}
|
||||
|
||||
$version = \SQLite3::version();
|
||||
|
||||
return $this->dataCache['version'] = $version['versionString'];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Execute the query
|
||||
*
|
||||
* @param string $sql
|
||||
*
|
||||
* @return mixed \SQLite3Result object or bool
|
||||
*/
|
||||
public function execute(string $sql)
|
||||
{
|
||||
return $this->isWriteType($sql)
|
||||
? $this->connID->exec($sql)
|
||||
: $this->connID->query($sql);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the total number of rows affected by this query.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function affectedRows(): int
|
||||
{
|
||||
return $this->connID->changes();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform-dependant string escape
|
||||
*
|
||||
* @param string $str
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _escapeString(string $str): string
|
||||
{
|
||||
return $this->connID->escapeString($str);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates the SQL for listing tables in a platform-dependent manner.
|
||||
*
|
||||
* @param boolean $prefixLimit
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _listTables(bool $prefixLimit = false): string
|
||||
{
|
||||
return 'SELECT "NAME" FROM "SQLITE_MASTER" WHERE "TYPE" = \'table\''
|
||||
. ' AND "NAME" NOT LIKE \'sqlite!_%\' ESCAPE \'!\''
|
||||
. (($prefixLimit !== false && $this->DBPrefix !== '')
|
||||
? ' AND "NAME" LIKE \'' . $this->escapeLikeString($this->DBPrefix) . '%\' ' . sprintf($this->likeEscapeStr,
|
||||
$this->likeEscapeChar)
|
||||
: '');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates a platform-specific query string so that the column names can be fetched.
|
||||
*
|
||||
* @param string $table
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _listColumns(string $table = ''): string
|
||||
{
|
||||
return 'PRAGMA TABLE_INFO(' . $this->protectIdentifiers($table, true, null, false) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch Field Names
|
||||
*
|
||||
* @param string $table Table name
|
||||
*
|
||||
* @return array|false
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function getFieldNames(string $table)
|
||||
{
|
||||
// Is there a cached result?
|
||||
if (isset($this->dataCache['field_names'][$table]))
|
||||
{
|
||||
return $this->dataCache['field_names'][$table];
|
||||
}
|
||||
|
||||
if (empty($this->connID))
|
||||
{
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
if (false === ($sql = $this->_listColumns($table)))
|
||||
{
|
||||
if ($this->DBDebug)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.featureUnavailable'));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = $this->query($sql);
|
||||
$this->dataCache['field_names'][$table] = [];
|
||||
|
||||
foreach ($query->getResultArray() as $row)
|
||||
{
|
||||
// Do we know from where to get the column's name?
|
||||
if (! isset($key))
|
||||
{
|
||||
if (isset($row['column_name']))
|
||||
{
|
||||
$key = 'column_name';
|
||||
}
|
||||
elseif (isset($row['COLUMN_NAME']))
|
||||
{
|
||||
$key = 'COLUMN_NAME';
|
||||
}
|
||||
elseif (isset($row['name']))
|
||||
{
|
||||
$key = 'name';
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have no other choice but to just get the first element's key.
|
||||
$key = key($row);
|
||||
}
|
||||
}
|
||||
|
||||
$this->dataCache['field_names'][$table][] = $row[$key];
|
||||
}
|
||||
|
||||
return $this->dataCache['field_names'][$table];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with field data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _fieldData(string $table): array
|
||||
{
|
||||
if (($query = $this->query('PRAGMA TABLE_INFO(' . $this->protectIdentifiers($table, true, null,
|
||||
false) . ')')) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetFieldData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
if (empty($query))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
$retVal = [];
|
||||
for ($i = 0, $c = count($query); $i < $c; $i++)
|
||||
{
|
||||
$retVal[$i] = new \stdClass();
|
||||
$retVal[$i]->name = $query[$i]->name;
|
||||
$retVal[$i]->type = $query[$i]->type;
|
||||
$retVal[$i]->max_length = null;
|
||||
$retVal[$i]->default = $query[$i]->dflt_value;
|
||||
$retVal[$i]->primary_key = isset($query[$i]->pk) ? (bool)$query[$i]->pk : false;
|
||||
$retVal[$i]->nullable = isset($query[$i]->notnull) ? ! (bool)$query[$i]->notnull : false;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with index data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function _indexData(string $table): array
|
||||
{
|
||||
// Get indexes
|
||||
// Don't use PRAGMA index_list, so we can preserve index order
|
||||
$sql = "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name=" . $this->escape(strtolower($table));
|
||||
if (($query = $this->query($sql)) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetIndexData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
$retVal = [];
|
||||
foreach ($query as $row)
|
||||
{
|
||||
$obj = new \stdClass();
|
||||
$obj->name = $row->name;
|
||||
|
||||
// Get fields for index
|
||||
$obj->fields = [];
|
||||
if (($fields = $this->query('PRAGMA index_info(' . $this->escape(strtolower($row->name)) . ')')) === false)
|
||||
{
|
||||
throw new DatabaseException(lang('Database.failGetIndexData'));
|
||||
}
|
||||
$fields = $fields->getResultObject();
|
||||
|
||||
foreach ($fields as $field)
|
||||
{
|
||||
$obj->fields[] = $field->name;
|
||||
}
|
||||
|
||||
$retVal[$obj->name] = $obj;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of objects with Foreign key data
|
||||
*
|
||||
* @param string $table
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
public function _foreignKeyData(string $table): array
|
||||
{
|
||||
if ($this->supportsForeignKeys() !== true)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$tables = $this->listTables();
|
||||
|
||||
if (empty($tables))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$retVal = [];
|
||||
|
||||
foreach ($tables as $table)
|
||||
{
|
||||
$query = $this->query("PRAGMA foreign_key_list({$table})")->getResult();
|
||||
|
||||
foreach ($query as $row)
|
||||
{
|
||||
$obj = new \stdClass();
|
||||
$obj->constraint_name = $row->from . ' to ' . $row->table . '.' . $row->to;
|
||||
$obj->table_name = $table;
|
||||
$obj->foreign_table_name = $row->table;
|
||||
$obj->sequence = $row->seq;
|
||||
|
||||
$retVal[] = $obj;
|
||||
}
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns platform-specific SQL to disable foreign key checks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _disableForeignKeyChecks()
|
||||
{
|
||||
return 'PRAGMA foreign_keys = OFF';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns platform-specific SQL to enable foreign key checks.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _enableForeignKeyChecks()
|
||||
{
|
||||
return 'PRAGMA foreign_keys = ON';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the last error code and message.
|
||||
*
|
||||
* Must return an array with keys 'code' and 'message':
|
||||
*
|
||||
* return ['code' => null, 'message' => null);
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function error(): array
|
||||
{
|
||||
return [
|
||||
'code' => $this->connID->lastErrorCode(),
|
||||
'message' => $this->connID->lastErrorMsg(),
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Insert ID
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function insertID(): int
|
||||
{
|
||||
return $this->connID->lastInsertRowID();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Begin Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transBegin(): bool
|
||||
{
|
||||
return $this->connID->exec('BEGIN TRANSACTION');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Commit Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transCommit(): bool
|
||||
{
|
||||
return $this->connID->exec('END TRANSACTION');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Rollback Transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _transRollback(): bool
|
||||
{
|
||||
return $this->connID->exec('ROLLBACK');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the statement is a write-type query or not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isWriteType($sql): bool
|
||||
{
|
||||
return (bool)preg_match(
|
||||
'/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX)\s/i',
|
||||
$sql);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks to see if the current install supports Foreign Keys
|
||||
* and has them enabled.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function supportsForeignKeys(): bool
|
||||
{
|
||||
$result = $this->simpleQuery('PRAGMA foreign_keys');
|
||||
|
||||
return (bool)$result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
333
system/Database/SQLite3/Forge.php
Normal file
333
system/Database/SQLite3/Forge.php
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Forge for SQLite3
|
||||
*/
|
||||
class Forge extends \CodeIgniter\Database\Forge
|
||||
{
|
||||
|
||||
/**
|
||||
* UNSIGNED support
|
||||
*
|
||||
* @var boolean|array
|
||||
*/
|
||||
protected $_unsigned = false;
|
||||
|
||||
/**
|
||||
* NULL value representation in CREATE/ALTER TABLE statements
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_null = 'NULL';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param $db ConnectionInterface
|
||||
*/
|
||||
public function __construct(ConnectionInterface $db)
|
||||
{
|
||||
parent::__construct($db);
|
||||
|
||||
if (version_compare($this->db->getVersion(), '3.3', '<'))
|
||||
{
|
||||
$this->createTableIfStr = false;
|
||||
$this->dropTableIfStr = false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create database
|
||||
*
|
||||
* @param string $dbName
|
||||
* @param boolean $ifNotExists Whether to add IF NOT EXISTS condition
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function createDatabase(string $dbName, bool $ifNotExists = false): bool
|
||||
{
|
||||
// In SQLite, a database is created when you connect to the database.
|
||||
// We'll return TRUE so that an error isn't generated.
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Drop database
|
||||
*
|
||||
* @param string $dbName
|
||||
*
|
||||
* @return boolean
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function dropDatabase(string $dbName): bool
|
||||
{
|
||||
// In SQLite, a database is dropped when we delete a file
|
||||
if (! is_file($dbName))
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('Unable to drop the specified database.');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to close the pseudo-connection first
|
||||
$this->db->close();
|
||||
if (! @unlink($dbName))
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
{
|
||||
throw new DatabaseException('Unable to drop the specified database.');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! empty($this->db->dataCache['db_names']))
|
||||
{
|
||||
$key = array_search(strtolower($dbName), array_map('strtolower', $this->db->dataCache['db_names']), true);
|
||||
if ($key !== false)
|
||||
{
|
||||
unset($this->db->dataCache['db_names'][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ALTER TABLE
|
||||
*
|
||||
* @param string $alter_type ALTER type
|
||||
* @param string $table Table name
|
||||
* @param mixed $field Column definition
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
protected function _alterTable(string $alter_type, string $table, $field)
|
||||
{
|
||||
switch ($alter_type)
|
||||
{
|
||||
case 'DROP':
|
||||
$sqlTable = new Table($this->db, $this);
|
||||
|
||||
$sqlTable->fromTable($table)
|
||||
->dropColumn($field)
|
||||
->run();
|
||||
|
||||
return '';
|
||||
break;
|
||||
case 'CHANGE':
|
||||
$sqlTable = new Table($this->db, $this);
|
||||
|
||||
$sqlTable->fromTable($table)
|
||||
->modifyColumn($field)
|
||||
->run();
|
||||
|
||||
return null;
|
||||
break;
|
||||
default:
|
||||
return parent::_alterTable($alter_type, $table, $field);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process column
|
||||
*
|
||||
* @param array $field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _processColumn(array $field): string
|
||||
{
|
||||
if ($field['type'] === 'TEXT' && strpos($field['length'], "('") === 0)
|
||||
{
|
||||
$field['type'] .= ' CHECK(' . $this->db->escapeIdentifiers($field['name'])
|
||||
. ' IN ' . $field['length'] . ')';
|
||||
}
|
||||
|
||||
return $this->db->escapeIdentifiers($field['name'])
|
||||
. ' ' . $field['type']
|
||||
. $field['auto_increment']
|
||||
. $field['null']
|
||||
. $field['unique']
|
||||
. $field['default'];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process indexes
|
||||
*
|
||||
* @param string $table
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function _processIndexes(string $table): array
|
||||
{
|
||||
$sqls = [];
|
||||
|
||||
for ($i = 0, $c = count($this->keys); $i < $c; $i++)
|
||||
{
|
||||
$this->keys[$i] = (array)$this->keys[$i];
|
||||
|
||||
for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
|
||||
{
|
||||
if (! isset($this->fields[$this->keys[$i][$i2]]))
|
||||
{
|
||||
unset($this->keys[$i][$i2]);
|
||||
}
|
||||
}
|
||||
if (count($this->keys[$i]) <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($i, $this->uniqueKeys))
|
||||
{
|
||||
$sqls[] = 'CREATE UNIQUE INDEX ' . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i]))
|
||||
. ' ON ' . $this->db->escapeIdentifiers($table)
|
||||
. ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ');';
|
||||
continue;
|
||||
}
|
||||
|
||||
$sqls[] = 'CREATE INDEX ' . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i]))
|
||||
. ' ON ' . $this->db->escapeIdentifiers($table)
|
||||
. ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ');';
|
||||
}
|
||||
|
||||
return $sqls;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Field attribute TYPE
|
||||
*
|
||||
* Performs a data type mapping between different databases.
|
||||
*
|
||||
* @param array &$attributes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _attributeType(array &$attributes)
|
||||
{
|
||||
switch (strtoupper($attributes['TYPE']))
|
||||
{
|
||||
case 'ENUM':
|
||||
case 'SET':
|
||||
$attributes['TYPE'] = 'TEXT';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Field attribute AUTO_INCREMENT
|
||||
*
|
||||
* @param array &$attributes
|
||||
* @param array &$field
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _attributeAutoIncrement(array &$attributes, array &$field)
|
||||
{
|
||||
if (! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === true
|
||||
&& stripos($field['type'], 'int') !== false)
|
||||
{
|
||||
$field['type'] = 'INTEGER PRIMARY KEY';
|
||||
$field['default'] = '';
|
||||
$field['null'] = '';
|
||||
$field['unique'] = '';
|
||||
$field['auto_increment'] = ' AUTOINCREMENT';
|
||||
|
||||
$this->primaryKeys = [];
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Foreign Key Drop
|
||||
*
|
||||
* @param string $table Table name
|
||||
* @param string $foreignName Foreign name
|
||||
*
|
||||
* @return boolean
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function dropForeignKey(string $table, string $foreignName): bool
|
||||
{
|
||||
// If this version of SQLite doesn't support it, we're done here
|
||||
if ($this->db->supportsForeignKeys() !== true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise we have to copy the table and recreate
|
||||
// without the foreign key being involved now
|
||||
$sqlTable = new Table($this->db, $this);
|
||||
|
||||
return $sqlTable->fromTable($this->db->DBPrefix . $table)
|
||||
->dropForeignKey($foreignName)
|
||||
->run();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
}
|
||||
143
system/Database/SQLite3/PreparedQuery.php
Normal file
143
system/Database/SQLite3/PreparedQuery.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\PreparedQueryInterface;
|
||||
use CodeIgniter\Database\BasePreparedQuery;
|
||||
|
||||
/**
|
||||
* Prepared query for SQLite3
|
||||
*/
|
||||
|
||||
class PreparedQuery extends BasePreparedQuery implements PreparedQueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* The SQLite3Result resource, or false.
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepares the query against the database, and saves the connection
|
||||
* info necessary to execute the query later.
|
||||
*
|
||||
* NOTE: This version is based on SQL code. Child classes should
|
||||
* override this method.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $options Passed to the connection's prepare statement.
|
||||
* Unused in the MySQLi driver.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _prepare(string $sql, array $options = [])
|
||||
{
|
||||
if (! ($this->statement = $this->db->connID->prepare($sql)))
|
||||
{
|
||||
$this->errorCode = $this->db->connID->lastErrorCode();
|
||||
$this->errorString = $this->db->connID->lastErrorMsg();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes a new set of data and runs it against the currently
|
||||
* prepared query. Upon success, will return a Results object.
|
||||
*
|
||||
* @todo finalize()
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function _execute(array $data): bool
|
||||
{
|
||||
if (is_null($this->statement))
|
||||
{
|
||||
throw new \BadMethodCallException('You must call prepare before trying to execute a prepared statement.');
|
||||
}
|
||||
|
||||
foreach ($data as $key => $item)
|
||||
{
|
||||
// Determine the type string
|
||||
if (is_integer($item))
|
||||
{
|
||||
$bindType = SQLITE3_INTEGER;
|
||||
}
|
||||
elseif (is_float($item))
|
||||
{
|
||||
$bindType = SQLITE3_FLOAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
$bindType = SQLITE3_TEXT;
|
||||
}
|
||||
|
||||
// Bind it
|
||||
$this->statement->bindValue($key + 1, $item, $bindType);
|
||||
}
|
||||
|
||||
$this->result = $this->statement->execute();
|
||||
|
||||
return $this->result !== false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result object for the prepared query.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
}
|
||||
205
system/Database/SQLite3/Result.php
Normal file
205
system/Database/SQLite3/Result.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\BaseResult;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
use CodeIgniter\Database\ResultInterface;
|
||||
use CodeIgniter\Entity;
|
||||
|
||||
/**
|
||||
* Result for SQLite3
|
||||
*/
|
||||
class Result extends BaseResult implements ResultInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets the number of fields in the result set.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getFieldCount(): int
|
||||
{
|
||||
return $this->resultID->numColumns();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of column names in the result set.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldNames(): array
|
||||
{
|
||||
$fieldNames = [];
|
||||
for ($i = 0, $c = $this->getFieldCount(); $i < $c; $i ++)
|
||||
{
|
||||
$fieldNames[] = $this->resultID->columnName($i);
|
||||
}
|
||||
|
||||
return $fieldNames;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates an array of objects representing field meta-data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldData(): array
|
||||
{
|
||||
static $data_types = [
|
||||
SQLITE3_INTEGER => 'integer',
|
||||
SQLITE3_FLOAT => 'float',
|
||||
SQLITE3_TEXT => 'text',
|
||||
SQLITE3_BLOB => 'blob',
|
||||
SQLITE3_NULL => 'null',
|
||||
];
|
||||
|
||||
$retVal = [];
|
||||
|
||||
for ($i = 0, $c = $this->getFieldCount(); $i < $c; $i ++)
|
||||
{
|
||||
$retVal[$i] = new \stdClass();
|
||||
$retVal[$i]->name = $this->resultID->columnName($i);
|
||||
$type = $this->resultID->columnType($i);
|
||||
$retVal[$i]->type = isset($data_types[$type]) ? $data_types[$type] : $type;
|
||||
$retVal[$i]->max_length = null;
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Frees the current result.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function freeResult()
|
||||
{
|
||||
if (is_object($this->resultID))
|
||||
{
|
||||
$this->resultID->finalize();
|
||||
$this->resultID = false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Moves the internal pointer to the desired offset. This is called
|
||||
* internally before fetching results to make sure the result set
|
||||
* starts at zero.
|
||||
*
|
||||
* @param integer $n
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
*/
|
||||
public function dataSeek(int $n = 0)
|
||||
{
|
||||
if ($n !== 0)
|
||||
{
|
||||
throw new DatabaseException('SQLite3 doesn\'t support seeking to other offset.');
|
||||
}
|
||||
|
||||
return $this->resultID->reset();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an array.
|
||||
*
|
||||
* Overridden by driver classes.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fetchAssoc()
|
||||
{
|
||||
return $this->resultID->fetchArray(SQLITE3_ASSOC);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the result set as an object.
|
||||
*
|
||||
* Overridden by child classes.
|
||||
*
|
||||
* @param string $className
|
||||
*
|
||||
* @return object|boolean
|
||||
*/
|
||||
protected function fetchObject(string $className = 'stdClass')
|
||||
{
|
||||
// No native support for fetching rows as objects
|
||||
if (($row = $this->fetchAssoc()) === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
elseif ($className === 'stdClass')
|
||||
{
|
||||
return (object) $row;
|
||||
}
|
||||
|
||||
$classObj = new $className();
|
||||
|
||||
if (is_subclass_of($className, Entity::class))
|
||||
{
|
||||
return $classObj->setAttributes($row);
|
||||
}
|
||||
|
||||
$classSet = \Closure::bind(function ($key, $value) {
|
||||
$this->$key = $value;
|
||||
}, $classObj, $className
|
||||
);
|
||||
foreach (array_keys($row) as $key)
|
||||
{
|
||||
$classSet($key, $row[$key]);
|
||||
}
|
||||
return $classObj;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
426
system/Database/SQLite3/Table.php
Normal file
426
system/Database/SQLite3/Table.php
Normal file
@@ -0,0 +1,426 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\Exceptions\DataException;
|
||||
|
||||
/**
|
||||
* Class Table
|
||||
*
|
||||
* Provides missing features for altering tables that are common
|
||||
* in other supported databases, but are missing from SQLite.
|
||||
* These are needed in order to support migrations during testing
|
||||
* when another database is used as the primary engine, but
|
||||
* SQLite in memory databases are used for faster test execution.
|
||||
*
|
||||
* @package CodeIgniter\Database\SQLite3
|
||||
*/
|
||||
class Table
|
||||
{
|
||||
/**
|
||||
* All of the fields this table represents.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = [];
|
||||
|
||||
/**
|
||||
* All of the unique/primary keys in the table.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $keys = [];
|
||||
|
||||
/**
|
||||
* All of the foreign keys in the table.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $foreignKeys = [];
|
||||
|
||||
/**
|
||||
* The name of the table we're working with.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tableName;
|
||||
|
||||
/**
|
||||
* The name of the table, with database prefix
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefixedTableName;
|
||||
|
||||
/**
|
||||
* Database connection.
|
||||
*
|
||||
* @var Connection
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Handle to our forge.
|
||||
*
|
||||
* @var Forge
|
||||
*/
|
||||
protected $forge;
|
||||
|
||||
/**
|
||||
* Table constructor.
|
||||
*
|
||||
* @param Connection $db
|
||||
* @param Forge $forge
|
||||
*/
|
||||
public function __construct(Connection $db, Forge $forge)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->forge = $forge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an existing database table and
|
||||
* collects all of the information needed to
|
||||
* recreate this table.
|
||||
*
|
||||
* @param string $table
|
||||
*
|
||||
* @return \CodeIgniter\Database\SQLite3\Table
|
||||
*/
|
||||
public function fromTable(string $table)
|
||||
{
|
||||
$this->prefixedTableName = $table;
|
||||
|
||||
// Remove the prefix, if any, since it's
|
||||
// already been added by the time we get here...
|
||||
$prefix = $this->db->DBPrefix;
|
||||
if (! empty($prefix))
|
||||
{
|
||||
if (strpos($table, $prefix) === 0)
|
||||
{
|
||||
$table = substr($table, strlen($prefix));
|
||||
}
|
||||
}
|
||||
|
||||
if (! $this->db->tableExists($this->prefixedTableName))
|
||||
{
|
||||
throw DataException::forTableNotFound($this->prefixedTableName);
|
||||
}
|
||||
|
||||
$this->tableName = $table;
|
||||
|
||||
$this->fields = $this->formatFields($this->db->getFieldData($table));
|
||||
|
||||
$this->keys = array_merge($this->keys, $this->formatKeys($this->db->getIndexData($table)));
|
||||
|
||||
$this->foreignKeys = $this->db->getForeignKeyData($table);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after `fromTable` and any actions, like `dropColumn`, etc,
|
||||
* to finalize the action. It creates a temp table, creates the new
|
||||
* table with modifications, and copies the data over to the new table.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function run(): bool
|
||||
{
|
||||
$this->db->query('PRAGMA foreign_keys = OFF');
|
||||
|
||||
$this->db->transStart();
|
||||
|
||||
$this->forge->renameTable($this->tableName, "temp_{$this->tableName}");
|
||||
|
||||
$this->forge->reset();
|
||||
|
||||
$this->createTable();
|
||||
|
||||
$this->copyData();
|
||||
|
||||
$this->forge->dropTable("temp_{$this->tableName}");
|
||||
|
||||
$success = $this->db->transComplete();
|
||||
|
||||
$this->db->query('PRAGMA foreign_keys = ON');
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a column from the table.
|
||||
*
|
||||
* @param string $column
|
||||
*
|
||||
* @return \CodeIgniter\Database\SQLite3\Table
|
||||
*/
|
||||
public function dropColumn(string $column)
|
||||
{
|
||||
unset($this->fields[$column]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies a field, including changing data type,
|
||||
* renaming, etc.
|
||||
*
|
||||
* @param array $field
|
||||
*
|
||||
* @return \CodeIgniter\Database\SQLite3\Table
|
||||
*/
|
||||
public function modifyColumn(array $field)
|
||||
{
|
||||
$field = $field[0];
|
||||
|
||||
$oldName = $field['name'];
|
||||
unset($field['name']);
|
||||
|
||||
$this->fields[$oldName] = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a foreign key from this table so that
|
||||
* it won't be recreated in the future.
|
||||
*
|
||||
* @param string $column
|
||||
*
|
||||
* @return \CodeIgniter\Database\SQLite3\Table
|
||||
*/
|
||||
public function dropForeignKey(string $column)
|
||||
{
|
||||
if (empty($this->foreignKeys))
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < count($this->foreignKeys); $i++)
|
||||
{
|
||||
if ($this->foreignKeys[$i]->table_name !== $this->tableName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// The column name should be the first thing in the constraint name
|
||||
if (strpos($this->foreignKeys[$i]->constraint_name, $column) !== 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($this->foreignKeys[$i]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the new table based on our current fields.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function createTable()
|
||||
{
|
||||
$this->dropIndexes();
|
||||
$this->db->resetDataCache();
|
||||
|
||||
// Handle any modified columns.
|
||||
$fields = [];
|
||||
foreach ($this->fields as $name => $field)
|
||||
{
|
||||
if (isset($field['new_name']))
|
||||
{
|
||||
$fields[$field['new_name']] = $field;
|
||||
continue;
|
||||
}
|
||||
|
||||
$fields[$name] = $field;
|
||||
}
|
||||
|
||||
$this->forge->addField($fields);
|
||||
|
||||
// Unique/Index keys
|
||||
if (is_array($this->keys))
|
||||
{
|
||||
foreach ($this->keys as $key)
|
||||
{
|
||||
switch ($key['type'])
|
||||
{
|
||||
case 'primary':
|
||||
$this->forge->addPrimaryKey($key['fields']);
|
||||
break;
|
||||
case 'unique':
|
||||
$this->forge->addUniqueKey($key['fields']);
|
||||
break;
|
||||
case 'index':
|
||||
$this->forge->addKey($key['fields']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Foreign Keys
|
||||
|
||||
return $this->forge->createTable($this->tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies data from our old table to the new one,
|
||||
* taking care map data correctly based on any columns
|
||||
* that have been renamed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function copyData()
|
||||
{
|
||||
$exFields = [];
|
||||
$newFields = [];
|
||||
|
||||
foreach ($this->fields as $name => $details)
|
||||
{
|
||||
// Are we modifying the column?
|
||||
if (isset($details['new_name']))
|
||||
{
|
||||
$newFields[] = $details['new_name'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$newFields[] = $name;
|
||||
}
|
||||
|
||||
$exFields[] = $name;
|
||||
}
|
||||
|
||||
$exFields = implode(', ', $exFields);
|
||||
$newFields = implode(', ', $newFields);
|
||||
|
||||
$this->db->query("INSERT INTO {$this->prefixedTableName}({$newFields}) SELECT {$exFields} FROM {$this->db->DBPrefix}temp_{$this->tableName}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts fields retrieved from the database to
|
||||
* the format needed for creating fields with Forge.
|
||||
*
|
||||
* @param array|boolean $fields
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function formatFields($fields)
|
||||
{
|
||||
if (! is_array($fields))
|
||||
{
|
||||
return $fields;
|
||||
}
|
||||
|
||||
$return = [];
|
||||
|
||||
foreach ($fields as $field)
|
||||
{
|
||||
$return[$field->name] = [
|
||||
'type' => $field->type,
|
||||
'default' => $field->default,
|
||||
'nullable' => $field->nullable,
|
||||
];
|
||||
|
||||
if ($field->primary_key)
|
||||
{
|
||||
$this->keys[$field->name] = [
|
||||
'fields' => [$field->name],
|
||||
'type' => 'primary',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts keys retrieved from the database to
|
||||
* the format needed to create later.
|
||||
*
|
||||
* @param mixed $keys
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function formatKeys($keys)
|
||||
{
|
||||
if (! is_array($keys))
|
||||
{
|
||||
return $keys;
|
||||
}
|
||||
|
||||
$return = [];
|
||||
|
||||
foreach ($keys as $name => $key)
|
||||
{
|
||||
$return[$name] = [
|
||||
'fields' => $key->fields,
|
||||
'type' => 'index',
|
||||
];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to drop all indexes and constraints
|
||||
* from the database for this table.
|
||||
*
|
||||
* @return null|void
|
||||
*/
|
||||
protected function dropIndexes()
|
||||
{
|
||||
if (! is_array($this->keys) || ! count($this->keys))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->keys as $name => $key)
|
||||
{
|
||||
if ($key['type'] === 'primary' || $key['type'] === 'unique')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->db->query("DROP INDEX IF EXISTS '{$name}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
73
system/Database/SQLite3/Utils.php
Normal file
73
system/Database/SQLite3/Utils.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database\SQLite3;
|
||||
|
||||
use CodeIgniter\Database\BaseUtils;
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
|
||||
/**
|
||||
* Utils for SQLite3
|
||||
*/
|
||||
class Utils extends BaseUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* OPTIMIZE TABLE statement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $optimizeTable = 'REINDEX %s';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Platform dependent version of the backup function.
|
||||
*
|
||||
* @param array|null $prefs
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function _backup(array $prefs = null)
|
||||
{
|
||||
throw new DatabaseException('Unsupported feature of the database platform you are using.');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
228
system/Database/Seeder.php
Normal file
228
system/Database/Seeder.php
Normal file
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Database;
|
||||
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
/**
|
||||
* Class Seeder
|
||||
*/
|
||||
class Seeder
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of the database group to use.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $DBGroup;
|
||||
|
||||
/**
|
||||
* Where we can find the Seed files.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $seedPath;
|
||||
|
||||
/**
|
||||
* An instance of the main Database configuration
|
||||
*
|
||||
* @var BaseConfig
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Database Connection instance
|
||||
*
|
||||
* @var BaseConnection
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Database Forge instance.
|
||||
*
|
||||
* @var Forge
|
||||
*/
|
||||
protected $forge;
|
||||
|
||||
/**
|
||||
* If true, will not display CLI messages.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $silent = false;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Seeder constructor.
|
||||
*
|
||||
* @param BaseConfig $config
|
||||
* @param BaseConnection $db
|
||||
*/
|
||||
public function __construct(BaseConfig $config, BaseConnection $db = null)
|
||||
{
|
||||
$this->seedPath = $config->filesPath ?? APPPATH . 'Database/';
|
||||
|
||||
if (empty($this->seedPath))
|
||||
{
|
||||
throw new \InvalidArgumentException('Invalid filesPath set in the Config\Database.');
|
||||
}
|
||||
|
||||
$this->seedPath = rtrim($this->seedPath, '/') . '/Seeds/';
|
||||
|
||||
if (! is_dir($this->seedPath))
|
||||
{
|
||||
throw new \InvalidArgumentException('Unable to locate the seeds directory. Please check Config\Database::filesPath');
|
||||
}
|
||||
|
||||
$this->config = & $config;
|
||||
|
||||
if (is_null($db))
|
||||
{
|
||||
$db = \Config\Database::connect($this->DBGroup);
|
||||
}
|
||||
|
||||
$this->db = & $db;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Loads the specified seeder and runs it.
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function call(string $class)
|
||||
{
|
||||
if (empty($class))
|
||||
{
|
||||
throw new \InvalidArgumentException('No Seeder was specified.');
|
||||
}
|
||||
|
||||
$path = str_replace('.php', '', $class) . '.php';
|
||||
|
||||
// If we have namespaced class, simply try to load it.
|
||||
if (strpos($class, '\\') !== false)
|
||||
{
|
||||
$seeder = new $class($this->config);
|
||||
}
|
||||
// Otherwise, try to load the class manually.
|
||||
else
|
||||
{
|
||||
$path = $this->seedPath . $path;
|
||||
|
||||
if (! is_file($path))
|
||||
{
|
||||
throw new \InvalidArgumentException('The specified Seeder is not a valid file: ' . $path);
|
||||
}
|
||||
|
||||
// Assume the class has the correct namespace
|
||||
$class = APP_NAMESPACE . '\Database\Seeds\\' . $class;
|
||||
|
||||
if (! class_exists($class, false))
|
||||
{
|
||||
require_once $path;
|
||||
}
|
||||
|
||||
$seeder = new $class($this->config);
|
||||
}
|
||||
|
||||
$seeder->run();
|
||||
|
||||
unset($seeder);
|
||||
|
||||
if (is_cli() && ! $this->silent)
|
||||
{
|
||||
CLI::write("Seeded: {$class}", 'green');
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sets the location of the directory that seed files can be located in.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return Seeder
|
||||
*/
|
||||
public function setPath(string $path)
|
||||
{
|
||||
$this->seedPath = rtrim($path, '/') . '/';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sets the silent treatment.
|
||||
*
|
||||
* @param boolean $silent
|
||||
*
|
||||
* @return Seeder
|
||||
*/
|
||||
public function setSilent(bool $silent)
|
||||
{
|
||||
$this->silent = $silent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Run the database seeds. This is where the magic happens.
|
||||
*
|
||||
* Child classes must implement this method and take care
|
||||
* of inserting their data here.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
500
system/Debug/Exceptions.php
Normal file
500
system/Debug/Exceptions.php
Normal file
@@ -0,0 +1,500 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug;
|
||||
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||
use CodeIgniter\HTTP\IncomingRequest;
|
||||
use CodeIgniter\HTTP\Response;
|
||||
use Config\Paths;
|
||||
use function error_reporting;
|
||||
use ErrorException;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Exceptions manager
|
||||
*/
|
||||
class Exceptions
|
||||
{
|
||||
|
||||
use ResponseTrait;
|
||||
|
||||
/**
|
||||
* Nesting level of the output buffering mechanism
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $ob_level;
|
||||
|
||||
/**
|
||||
* The path to the directory containing the
|
||||
* cli and html error view directories.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $viewPath;
|
||||
|
||||
/**
|
||||
* Config for debug exceptions.
|
||||
*
|
||||
* @var \Config\Exceptions
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* The incoming request.
|
||||
*
|
||||
* @var \CodeIgniter\HTTP\IncomingRequest
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* The outgoing response.
|
||||
*
|
||||
* @var \CodeIgniter\HTTP\Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Config\Exceptions $config
|
||||
* @param \CodeIgniter\HTTP\IncomingRequest $request
|
||||
* @param \CodeIgniter\HTTP\Response $response
|
||||
*/
|
||||
public function __construct(\Config\Exceptions $config, IncomingRequest $request, Response $response)
|
||||
{
|
||||
$this->ob_level = ob_get_level();
|
||||
|
||||
$this->viewPath = rtrim($config->errorViewPath, '/ ') . '/';
|
||||
|
||||
$this->config = $config;
|
||||
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Responsible for registering the error, exception and shutdown
|
||||
* handling of our application.
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
//Set the Exception Handler
|
||||
set_exception_handler([$this, 'exceptionHandler']);
|
||||
|
||||
// Set the Error Handler
|
||||
set_error_handler([$this, 'errorHandler']);
|
||||
|
||||
// Set the handler for shutdown to catch Parse errors
|
||||
// Do we need this in PHP7?
|
||||
register_shutdown_function([$this, 'shutdownHandler']);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Catches any uncaught errors and exceptions, including most Fatal errors
|
||||
* (Yay PHP7!). Will log the error, display it if display_errors is on,
|
||||
* and fire an event that allows custom actions to be taken at this point.
|
||||
*
|
||||
* @param \Throwable $exception
|
||||
*/
|
||||
public function exceptionHandler(Throwable $exception)
|
||||
{
|
||||
$codes = $this->determineCodes($exception);
|
||||
$statusCode = $codes[0];
|
||||
$exitCode = $codes[1];
|
||||
|
||||
// Log it
|
||||
if ($this->config->log === true && ! in_array($statusCode, $this->config->ignoreCodes))
|
||||
{
|
||||
log_message('critical', $exception->getMessage() . "\n{trace}", [
|
||||
'trace' => $exception->getTraceAsString(),
|
||||
]);
|
||||
}
|
||||
|
||||
if (! is_cli())
|
||||
{
|
||||
$this->response->setStatusCode($statusCode);
|
||||
$header = "HTTP/{$this->request->getProtocolVersion()} {$this->response->getStatusCode()} {$this->response->getReason()}";
|
||||
header($header, true, $statusCode);
|
||||
|
||||
if (strpos($this->request->getHeaderLine('accept'), 'text/html') === false)
|
||||
{
|
||||
$this->respond(ENVIRONMENT === 'development' ? $this->collectVars($exception, $statusCode) : '', $statusCode)->send();
|
||||
|
||||
exit($exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
$this->render($exception, $statusCode);
|
||||
|
||||
exit($exitCode);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Even in PHP7, some errors make it through to the errorHandler, so
|
||||
* convert these to Exceptions and let the exception handler log it and
|
||||
* display it.
|
||||
*
|
||||
* This seems to be primarily when a user triggers it with trigger_error().
|
||||
*
|
||||
* @param integer $severity
|
||||
* @param string $message
|
||||
* @param string|null $file
|
||||
* @param integer|null $line
|
||||
* @param null $context
|
||||
*
|
||||
* @throws \ErrorException
|
||||
*/
|
||||
public function errorHandler(int $severity, string $message, string $file = null, int $line = null, $context = null)
|
||||
{
|
||||
if (! (error_reporting() & $severity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert it to an exception and pass it along.
|
||||
throw new ErrorException($message, 0, $severity, $file, $line);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks to see if any errors have happened during shutdown that
|
||||
* need to be caught and handle them.
|
||||
*/
|
||||
public function shutdownHandler()
|
||||
{
|
||||
$error = error_get_last();
|
||||
|
||||
// If we've got an error that hasn't been displayed, then convert
|
||||
// it to an Exception and use the Exception handler to display it
|
||||
// to the user.
|
||||
if (! is_null($error))
|
||||
{
|
||||
// Fatal Error?
|
||||
if (in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]))
|
||||
{
|
||||
$this->exceptionHandler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines the view to display based on the exception thrown,
|
||||
* whether an HTTP or CLI request, etc.
|
||||
*
|
||||
* @param \Throwable $exception
|
||||
* @param string $template_path
|
||||
*
|
||||
* @return string The path and filename of the view file to use
|
||||
*/
|
||||
protected function determineView(Throwable $exception, string $template_path): string
|
||||
{
|
||||
// Production environments should have a custom exception file.
|
||||
$view = 'production.php';
|
||||
$template_path = rtrim($template_path, '/ ') . '/';
|
||||
|
||||
if (str_ireplace(['off', 'none', 'no', 'false', 'null'], '', ini_get('display_errors')))
|
||||
{
|
||||
$view = 'error_exception.php';
|
||||
}
|
||||
|
||||
// 404 Errors
|
||||
if ($exception instanceof PageNotFoundException)
|
||||
{
|
||||
return 'error_404.php';
|
||||
}
|
||||
|
||||
// Allow for custom views based upon the status code
|
||||
else if (is_file($template_path . 'error_' . $exception->getCode() . '.php'))
|
||||
{
|
||||
return 'error_' . $exception->getCode() . '.php';
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Given an exception and status code will display the error to the client.
|
||||
*
|
||||
* @param \Throwable $exception
|
||||
* @param integer $statusCode
|
||||
*/
|
||||
protected function render(Throwable $exception, int $statusCode)
|
||||
{
|
||||
// Determine directory with views
|
||||
$path = $this->viewPath;
|
||||
if (empty($path))
|
||||
{
|
||||
$paths = new Paths();
|
||||
$path = $paths->viewDirectory . '/errors/';
|
||||
}
|
||||
|
||||
$path = is_cli() ? $path . 'cli/' : $path . 'html/';
|
||||
|
||||
// Determine the vew
|
||||
$view = $this->determineView($exception, $path);
|
||||
|
||||
// Prepare the vars
|
||||
$vars = $this->collectVars($exception, $statusCode);
|
||||
extract($vars);
|
||||
|
||||
// Render it
|
||||
if (ob_get_level() > $this->ob_level + 1)
|
||||
{
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
ob_start();
|
||||
include($path . $view);
|
||||
$buffer = ob_get_contents();
|
||||
ob_end_clean();
|
||||
echo $buffer;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gathers the variables that will be made available to the view.
|
||||
*
|
||||
* @param \Throwable $exception
|
||||
* @param integer $statusCode
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function collectVars(Throwable $exception, int $statusCode): array
|
||||
{
|
||||
return [
|
||||
'title' => get_class($exception),
|
||||
'type' => get_class($exception),
|
||||
'code' => $statusCode,
|
||||
'message' => $exception->getMessage() ?? '(null)',
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
'trace' => $exception->getTrace(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the HTTP status code and the exit status code for this request.
|
||||
*
|
||||
* @param \Throwable $exception
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function determineCodes(Throwable $exception): array
|
||||
{
|
||||
$statusCode = abs($exception->getCode());
|
||||
|
||||
if ($statusCode < 100 || $statusCode > 599)
|
||||
{
|
||||
$exitStatus = $statusCode + EXIT__AUTO_MIN; // 9 is EXIT__AUTO_MIN
|
||||
if ($exitStatus > EXIT__AUTO_MAX) // 125 is EXIT__AUTO_MAX
|
||||
{
|
||||
$exitStatus = EXIT_ERROR; // EXIT_ERROR
|
||||
}
|
||||
$statusCode = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
$exitStatus = 1; // EXIT_ERROR
|
||||
}
|
||||
|
||||
return [
|
||||
$statusCode ?? 500,
|
||||
$exitStatus,
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------
|
||||
// Display Methods
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Clean Path
|
||||
*
|
||||
* This makes nicer looking paths for the error output.
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function cleanPath(string $file): string
|
||||
{
|
||||
if (strpos($file, APPPATH) === 0)
|
||||
{
|
||||
$file = 'APPPATH/' . substr($file, strlen(APPPATH));
|
||||
}
|
||||
elseif (strpos($file, SYSTEMPATH) === 0)
|
||||
{
|
||||
$file = 'SYSTEMPATH/' . substr($file, strlen(SYSTEMPATH));
|
||||
}
|
||||
elseif (strpos($file, FCPATH) === 0)
|
||||
{
|
||||
$file = 'FCPATH/' . substr($file, strlen(FCPATH));
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Describes memory usage in real-world units. Intended for use
|
||||
* with memory_get_usage, etc.
|
||||
*
|
||||
* @param $bytes
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function describeMemory(int $bytes): string
|
||||
{
|
||||
if ($bytes < 1024)
|
||||
{
|
||||
return $bytes . 'B';
|
||||
}
|
||||
else if ($bytes < 1048576)
|
||||
{
|
||||
return round($bytes / 1024, 2) . 'KB';
|
||||
}
|
||||
|
||||
return round($bytes / 1048576, 2) . 'MB';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a syntax-highlighted version of a PHP file.
|
||||
*
|
||||
* @param string $file
|
||||
* @param integer $lineNumber
|
||||
* @param integer $lines
|
||||
*
|
||||
* @return boolean|string
|
||||
*/
|
||||
public static function highlightFile(string $file, int $lineNumber, int $lines = 15)
|
||||
{
|
||||
if (empty($file) || ! is_readable($file))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set our highlight colors:
|
||||
if (function_exists('ini_set'))
|
||||
{
|
||||
ini_set('highlight.comment', '#767a7e; font-style: italic');
|
||||
ini_set('highlight.default', '#c7c7c7');
|
||||
ini_set('highlight.html', '#06B');
|
||||
ini_set('highlight.keyword', '#f1ce61;');
|
||||
ini_set('highlight.string', '#869d6a');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$source = file_get_contents($file);
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$source = str_replace(["\r\n", "\r"], "\n", $source);
|
||||
$source = explode("\n", highlight_string($source, true));
|
||||
$source = str_replace('<br />', "\n", $source[1]);
|
||||
|
||||
$source = explode("\n", str_replace("\r\n", "\n", $source));
|
||||
|
||||
// Get just the part to show
|
||||
$start = $lineNumber - (int) round($lines / 2);
|
||||
$start = $start < 0 ? 0 : $start;
|
||||
|
||||
// Get just the lines we need to display, while keeping line numbers...
|
||||
$source = array_splice($source, $start, $lines, true);
|
||||
|
||||
// Used to format the line number in the source
|
||||
$format = '% ' . strlen(sprintf('%s', $start + $lines)) . 'd';
|
||||
|
||||
$out = '';
|
||||
// Because the highlighting may have an uneven number
|
||||
// of open and close span tags on one line, we need
|
||||
// to ensure we can close them all to get the lines
|
||||
// showing correctly.
|
||||
$spans = 1;
|
||||
|
||||
foreach ($source as $n => $row)
|
||||
{
|
||||
$spans += substr_count($row, '<span') - substr_count($row, '</span');
|
||||
$row = str_replace(["\r", "\n"], ['', ''], $row);
|
||||
|
||||
if (($n + $start + 1) === $lineNumber)
|
||||
{
|
||||
preg_match_all('#<[^>]+>#', $row, $tags);
|
||||
$out .= sprintf("<span class='line highlight'><span class='number'>{$format}</span> %s\n</span>%s", $n + $start + 1, strip_tags($row), implode('', $tags[0])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$out .= sprintf('<span class="line"><span class="number">' . $format . '</span> %s', $n + $start + 1, $row) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($spans > 0)
|
||||
{
|
||||
$out .= str_repeat('</span>', $spans);
|
||||
}
|
||||
|
||||
return '<pre><code>' . $out . '</code></pre>';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
179
system/Debug/Iterator.php
Normal file
179
system/Debug/Iterator.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug;
|
||||
|
||||
/**
|
||||
* Iterator for debugging.
|
||||
*/
|
||||
class Iterator
|
||||
{
|
||||
|
||||
/**
|
||||
* Stores the tests that we are to run.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tests = [];
|
||||
|
||||
/**
|
||||
* Stores the results of each of the tests.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $results = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Adds a test to run.
|
||||
*
|
||||
* Tests are simply closures that the user can define any sequence of
|
||||
* things to happen during the test.
|
||||
*
|
||||
* @param string $name
|
||||
* @param \Closure $closure
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function add(string $name, \Closure $closure)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
|
||||
$this->tests[$name] = $closure;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Runs through all of the tests that have been added, recording
|
||||
* time to execute the desired number of iterations, and the approximate
|
||||
* memory usage used during those iterations.
|
||||
*
|
||||
* @param integer $iterations
|
||||
* @param boolean $output
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function run(int $iterations = 1000, bool $output = true)
|
||||
{
|
||||
foreach ($this->tests as $name => $test)
|
||||
{
|
||||
// clear memory before start
|
||||
gc_collect_cycles();
|
||||
|
||||
$start = microtime(true);
|
||||
$start_mem = $max_memory = memory_get_usage(true);
|
||||
|
||||
for ($i = 0; $i < $iterations; $i ++)
|
||||
{
|
||||
$result = $test();
|
||||
|
||||
$max_memory = max($max_memory, memory_get_usage(true));
|
||||
|
||||
unset($result);
|
||||
}
|
||||
|
||||
$this->results[$name] = [
|
||||
'time' => microtime(true) - $start,
|
||||
'memory' => $max_memory - $start_mem,
|
||||
'n' => $iterations,
|
||||
];
|
||||
}
|
||||
|
||||
if ($output)
|
||||
{
|
||||
return $this->getReport();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get results.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReport(): string
|
||||
{
|
||||
if (empty($this->results))
|
||||
{
|
||||
return 'No results to display.';
|
||||
}
|
||||
|
||||
helper('number');
|
||||
|
||||
// Template
|
||||
$tpl = '<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Test</td>
|
||||
<td>Time</td>
|
||||
<td>Memory</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows}
|
||||
</tbody>
|
||||
</table>';
|
||||
|
||||
$rows = '';
|
||||
|
||||
foreach ($this->results as $name => $result)
|
||||
{
|
||||
$memory = number_to_size($result['memory'], 4);
|
||||
|
||||
$rows .= "<tr>
|
||||
<td>{$name}</td>
|
||||
<td>" . number_format($result['time'], 4) . "</td>
|
||||
<td>{$memory}</td>
|
||||
</tr>";
|
||||
}
|
||||
|
||||
$tpl = str_replace('{rows}', $rows, $tpl);
|
||||
|
||||
return $tpl . '<br/>';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
}
|
||||
184
system/Debug/Timer.php
Normal file
184
system/Debug/Timer.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug;
|
||||
|
||||
/**
|
||||
* Class Timer
|
||||
*
|
||||
* Provides a simple way to measure the amount of time
|
||||
* that elapses between two points.
|
||||
*
|
||||
* NOTE: All methods are static since the class is intended
|
||||
* to measure throughout an entire application's life cycle.
|
||||
*
|
||||
* @package CodeIgniter\Benchmark
|
||||
*/
|
||||
class Timer
|
||||
{
|
||||
|
||||
/**
|
||||
* List of all timers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $timers = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Starts a timer running.
|
||||
*
|
||||
* Multiple calls can be made to this method so that several
|
||||
* execution points can be measured.
|
||||
*
|
||||
* @param string $name The name of this timer.
|
||||
* @param float $time Allows user to provide time.
|
||||
*
|
||||
* @return Timer
|
||||
*/
|
||||
public function start(string $name, float $time = null)
|
||||
{
|
||||
$this->timers[strtolower($name)] = [
|
||||
'start' => ! empty($time) ? $time : microtime(true),
|
||||
'end' => null,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Stops a running timer.
|
||||
*
|
||||
* If the timer is not stopped before the timers() method is called,
|
||||
* it will be automatically stopped at that point.
|
||||
*
|
||||
* @param string $name The name of this timer.
|
||||
*
|
||||
* @return Timer
|
||||
*/
|
||||
public function stop(string $name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
|
||||
if (empty($this->timers[$name]))
|
||||
{
|
||||
throw new \RuntimeException('Cannot stop timer: invalid name given.');
|
||||
}
|
||||
|
||||
$this->timers[$name]['end'] = microtime(true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the duration of a recorded timer.
|
||||
*
|
||||
* @param string $name The name of the timer.
|
||||
* @param integer $decimals Number of decimal places.
|
||||
*
|
||||
* @return null|float Returns null if timer exists by that name.
|
||||
* Returns a float representing the number of
|
||||
* seconds elapsed while that timer was running.
|
||||
*/
|
||||
public function getElapsedTime(string $name, int $decimals = 4)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
|
||||
if (empty($this->timers[$name]))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$timer = $this->timers[$name];
|
||||
|
||||
if (empty($timer['end']))
|
||||
{
|
||||
$timer['end'] = microtime(true);
|
||||
}
|
||||
|
||||
return (float) number_format($timer['end'] - $timer['start'], $decimals);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the array of timers, with the duration pre-calculated for you.
|
||||
*
|
||||
* @param integer $decimals Number of decimal places
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTimers(int $decimals = 4): array
|
||||
{
|
||||
$timers = $this->timers;
|
||||
|
||||
foreach ($timers as &$timer)
|
||||
{
|
||||
if (empty($timer['end']))
|
||||
{
|
||||
$timer['end'] = microtime(true);
|
||||
}
|
||||
|
||||
$timer['duration'] = (float) number_format($timer['end'] - $timer['start'], $decimals);
|
||||
}
|
||||
|
||||
return $timers;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether or not a timer with the specified name exists.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return array_key_exists(strtolower($name), $this->timers);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
512
system/Debug/Toolbar.php
Normal file
512
system/Debug/Toolbar.php
Normal file
@@ -0,0 +1,512 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use CodeIgniter\Debug\Toolbar\Collectors\History;
|
||||
use CodeIgniter\Format\JSONFormatter;
|
||||
use CodeIgniter\Format\XMLFormatter;
|
||||
use CodeIgniter\HTTP\DownloadResponse;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Config\Services;
|
||||
|
||||
/**
|
||||
* Debug Toolbar
|
||||
*
|
||||
* Displays a toolbar with bits of stats to aid a developer in debugging.
|
||||
*
|
||||
* Inspiration: http://prophiler.fabfuel.de
|
||||
*
|
||||
* @package CodeIgniter\Debug
|
||||
*/
|
||||
class Toolbar
|
||||
{
|
||||
|
||||
/**
|
||||
* Toolbar configuration settings.
|
||||
*
|
||||
* @var BaseConfig
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Collectors to be used and displayed.
|
||||
*
|
||||
* @var \CodeIgniter\Debug\Toolbar\Collectors\BaseCollector[]
|
||||
*/
|
||||
protected $collectors = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param BaseConfig $config
|
||||
*/
|
||||
public function __construct(BaseConfig $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
|
||||
foreach ($config->collectors as $collector)
|
||||
{
|
||||
if (! class_exists($collector))
|
||||
{
|
||||
log_message('critical', 'Toolbar collector does not exists(' . $collector . ').' .
|
||||
'please check $collectors in the Config\Toolbar.php file.');
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->collectors[] = new $collector();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns all the data required by Debug Bar
|
||||
*
|
||||
* @param float $startTime App start time
|
||||
* @param float $totalTime
|
||||
* @param \CodeIgniter\HTTP\RequestInterface $request
|
||||
* @param \CodeIgniter\HTTP\ResponseInterface $response
|
||||
*
|
||||
* @return string JSON encoded data
|
||||
*/
|
||||
public function run(float $startTime, float $totalTime, RequestInterface $request, ResponseInterface $response): string
|
||||
{
|
||||
// Data items used within the view.
|
||||
$data['url'] = current_url();
|
||||
$data['method'] = $request->getMethod(true);
|
||||
$data['isAJAX'] = $request->isAJAX();
|
||||
$data['startTime'] = $startTime;
|
||||
$data['totalTime'] = $totalTime * 1000;
|
||||
$data['totalMemory'] = number_format((memory_get_peak_usage()) / 1024 / 1024, 3);
|
||||
$data['segmentDuration'] = $this->roundTo($data['totalTime'] / 7, 5);
|
||||
$data['segmentCount'] = (int) ceil($data['totalTime'] / $data['segmentDuration']);
|
||||
$data['CI_VERSION'] = \CodeIgniter\CodeIgniter::CI_VERSION;
|
||||
$data['collectors'] = [];
|
||||
|
||||
foreach ($this->collectors as $collector)
|
||||
{
|
||||
$data['collectors'][] = $collector->getAsArray();
|
||||
}
|
||||
|
||||
foreach ($this->collectVarData() as $heading => $items)
|
||||
{
|
||||
$varData = [];
|
||||
|
||||
if (is_array($items))
|
||||
{
|
||||
foreach ($items as $key => $value)
|
||||
{
|
||||
$varData[esc($key)] = is_string($value) ? esc($value) : print_r($value, true);
|
||||
}
|
||||
}
|
||||
|
||||
$data['vars']['varData'][esc($heading)] = $varData;
|
||||
}
|
||||
|
||||
if (! empty($_SESSION))
|
||||
{
|
||||
foreach ($_SESSION as $key => $value)
|
||||
{
|
||||
// Replace the binary data with string to avoid json_encode failure.
|
||||
if (is_string($value) && preg_match('~[^\x20-\x7E\t\r\n]~', $value))
|
||||
{
|
||||
$value = 'binary data';
|
||||
}
|
||||
|
||||
$data['vars']['session'][esc($key)] = is_string($value) ? esc($value) : print_r($value, true);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($request->getGet() as $name => $value)
|
||||
{
|
||||
$data['vars']['get'][esc($name)] = is_array($value) ? esc(print_r($value, true)) : esc($value);
|
||||
}
|
||||
|
||||
foreach ($request->getPost() as $name => $value)
|
||||
{
|
||||
$data['vars']['post'][esc($name)] = is_array($value) ? esc(print_r($value, true)) : esc($value);
|
||||
}
|
||||
|
||||
foreach ($request->getHeaders() as $header => $value)
|
||||
{
|
||||
if (empty($value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_array($value))
|
||||
{
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
foreach ($value as $h)
|
||||
{
|
||||
$data['vars']['headers'][esc($h->getName())] = esc($h->getValueLine());
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($request->getCookie() as $name => $value)
|
||||
{
|
||||
$data['vars']['cookies'][esc($name)] = esc($value);
|
||||
}
|
||||
|
||||
$data['vars']['request'] = ($request->isSecure() ? 'HTTPS' : 'HTTP') . '/' . $request->getProtocolVersion();
|
||||
|
||||
$data['vars']['response'] = [
|
||||
'statusCode' => $response->getStatusCode(),
|
||||
'reason' => esc($response->getReason()),
|
||||
'contentType' => esc($response->getHeaderLine('content-type')),
|
||||
];
|
||||
|
||||
$data['config'] = \CodeIgniter\Debug\Toolbar\Collectors\Config::display();
|
||||
|
||||
if ($response->CSP !== null)
|
||||
{
|
||||
$response->CSP->addImageSrc('data:');
|
||||
}
|
||||
|
||||
return json_encode($data);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Called within the view to display the timeline itself.
|
||||
*
|
||||
* @param array $collectors
|
||||
* @param float $startTime
|
||||
* @param integer $segmentCount
|
||||
* @param integer $segmentDuration
|
||||
* @param array $styles
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function renderTimeline(array $collectors, float $startTime, int $segmentCount, int $segmentDuration, array &$styles): string
|
||||
{
|
||||
$displayTime = $segmentCount * $segmentDuration;
|
||||
$rows = $this->collectTimelineData($collectors);
|
||||
$output = '';
|
||||
$styleCount = 0;
|
||||
|
||||
foreach ($rows as $row)
|
||||
{
|
||||
$output .= '<tr>';
|
||||
$output .= "<td>{$row['name']}</td>";
|
||||
$output .= "<td>{$row['component']}</td>";
|
||||
$output .= "<td class='debug-bar-alignRight'>" . number_format($row['duration'] * 1000, 2) . ' ms</td>';
|
||||
$output .= "<td class='debug-bar-noverflow' colspan='{$segmentCount}'>";
|
||||
|
||||
$offset = ((((float) $row['start'] - $startTime) * 1000) / $displayTime) * 100;
|
||||
$length = (((float) $row['duration'] * 1000) / $displayTime) * 100;
|
||||
|
||||
$styles['debug-bar-timeline-' . $styleCount] = "left: {$offset}%; width: {$length}%;";
|
||||
$output .= "<span class='timer debug-bar-timeline-{$styleCount}' title='" . number_format($length, 2) . "%'></span>";
|
||||
$output .= '</td>';
|
||||
$output .= '</tr>';
|
||||
|
||||
$styleCount ++;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a sorted array of timeline data arrays from the collectors.
|
||||
*
|
||||
* @param array $collectors
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function collectTimelineData($collectors): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
// Collect it
|
||||
foreach ($collectors as $collector)
|
||||
{
|
||||
if (! $collector['hasTimelineData'])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = array_merge($data, $collector['timelineData']);
|
||||
}
|
||||
|
||||
// Sort it
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns an array of data from all of the modules
|
||||
* that should be displayed in the 'Vars' tab.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function collectVarData(): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->collectors as $collector)
|
||||
{
|
||||
if (! $collector->hasVarData())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = array_merge($data, $collector->getVarData());
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Rounds a number to the nearest incremental value.
|
||||
*
|
||||
* @param float $number
|
||||
* @param integer $increments
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected function roundTo(float $number, int $increments = 5): float
|
||||
{
|
||||
$increments = 1 / $increments;
|
||||
|
||||
return (ceil($number * $increments) / $increments);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepare for debugging..
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @global type $app
|
||||
* @return type
|
||||
*/
|
||||
public function prepare(RequestInterface $request = null, ResponseInterface $response = null)
|
||||
{
|
||||
if (CI_DEBUG && ! is_cli())
|
||||
{
|
||||
global $app;
|
||||
|
||||
$request = $request ?? Services::request();
|
||||
$response = $response ?? Services::response();
|
||||
|
||||
// Disable the toolbar for downloads
|
||||
if ($response instanceof DownloadResponse)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$toolbar = Services::toolbar(config(Toolbar::class));
|
||||
$stats = $app->getPerformanceStats();
|
||||
$data = $toolbar->run(
|
||||
$stats['startTime'],
|
||||
$stats['totalTime'],
|
||||
$request,
|
||||
$response
|
||||
);
|
||||
|
||||
helper('filesystem');
|
||||
|
||||
// Updated to time() so we can get history
|
||||
$time = time();
|
||||
|
||||
if (! is_dir(WRITEPATH . 'debugbar'))
|
||||
{
|
||||
mkdir(WRITEPATH . 'debugbar', 0777);
|
||||
}
|
||||
|
||||
write_file(WRITEPATH . 'debugbar/' . 'debugbar_' . $time . '.json', $data, 'w+');
|
||||
|
||||
$format = $response->getHeaderLine('content-type');
|
||||
|
||||
// Non-HTML formats should not include the debugbar
|
||||
// then we send headers saying where to find the debug data
|
||||
// for this response
|
||||
if ($request->isAJAX() || strpos($format, 'html') === false)
|
||||
{
|
||||
$response->setHeader('Debugbar-Time', $time)
|
||||
->setHeader('Debugbar-Link', site_url("?debugbar_time={$time}"))
|
||||
->getBody();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$script = PHP_EOL
|
||||
. '<script type="text/javascript" {csp-script-nonce} id="debugbar_loader" '
|
||||
. 'data-time="' . $time . '" '
|
||||
. 'src="' . site_url() . '?debugbar"></script>'
|
||||
. '<script type="text/javascript" {csp-script-nonce} id="debugbar_dynamic_script"></script>'
|
||||
. '<style type="text/css" {csp-style-nonce} id="debugbar_dynamic_style"></style>'
|
||||
. PHP_EOL;
|
||||
|
||||
if (strpos($response->getBody(), '</body>') !== false)
|
||||
{
|
||||
$response->setBody(
|
||||
str_replace('</body>', $script . '</body>', $response->getBody())
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$response->appendBody($script);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Inject debug toolbar into the response.
|
||||
*/
|
||||
public function respond()
|
||||
{
|
||||
if (ENVIRONMENT === 'testing')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$request = Services::request();
|
||||
|
||||
// If the request contains '?debugbar then we're
|
||||
// simply returning the loading script
|
||||
if ($request->getGet('debugbar') !== null)
|
||||
{
|
||||
// Let the browser know that we are sending javascript
|
||||
header('Content-Type: application/javascript');
|
||||
|
||||
ob_start();
|
||||
include($this->config->viewsPath . 'toolbarloader.js.php');
|
||||
$output = ob_get_clean();
|
||||
|
||||
exit($output);
|
||||
}
|
||||
|
||||
// Otherwise, if it includes ?debugbar_time, then
|
||||
// we should return the entire debugbar.
|
||||
if ($request->getGet('debugbar_time'))
|
||||
{
|
||||
helper('security');
|
||||
|
||||
// Negotiate the content-type to format the output
|
||||
$format = $request->negotiate('media', [
|
||||
'text/html',
|
||||
'application/json',
|
||||
'application/xml',
|
||||
]);
|
||||
$format = explode('/', $format)[1];
|
||||
|
||||
$file = sanitize_filename('debugbar_' . $request->getGet('debugbar_time'));
|
||||
$filename = WRITEPATH . 'debugbar/' . $file . '.json';
|
||||
|
||||
// Show the toolbar
|
||||
if (is_file($filename))
|
||||
{
|
||||
$contents = $this->format(file_get_contents($filename), $format);
|
||||
exit($contents);
|
||||
}
|
||||
|
||||
// File was not written or do not exists
|
||||
http_response_code(404);
|
||||
exit; // Exit here is needed to avoid load the index page
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format output
|
||||
*
|
||||
* @param string $data JSON encoded Toolbar data
|
||||
* @param string $format html, json, xml
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function format(string $data, string $format = 'html'): string
|
||||
{
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if ($this->config->maxHistory !== 0)
|
||||
{
|
||||
$history = new History();
|
||||
$history->setFiles(
|
||||
Services::request()->getGet('debugbar_time'),
|
||||
$this->config->maxHistory
|
||||
);
|
||||
|
||||
$data['collectors'][] = $history->getAsArray();
|
||||
}
|
||||
|
||||
$output = '';
|
||||
|
||||
switch ($format)
|
||||
{
|
||||
case 'html':
|
||||
$data['styles'] = [];
|
||||
extract($data);
|
||||
$parser = Services::parser($this->config->viewsPath, null, false);
|
||||
ob_start();
|
||||
include($this->config->viewsPath . 'toolbar.tpl.php');
|
||||
$output = ob_get_clean();
|
||||
break;
|
||||
case 'json':
|
||||
$formatter = new JSONFormatter();
|
||||
$output = $formatter->format($data);
|
||||
break;
|
||||
case 'xml':
|
||||
$formatter = new XMLFormatter;
|
||||
$output = $formatter->format($data);
|
||||
break;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
}
|
||||
329
system/Debug/Toolbar/Collectors/BaseCollector.php
Normal file
329
system/Debug/Toolbar/Collectors/BaseCollector.php
Normal file
@@ -0,0 +1,329 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug\Toolbar\Collectors;
|
||||
|
||||
/**
|
||||
* Base Toolbar collector
|
||||
*/
|
||||
class BaseCollector
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether this collector has data that can
|
||||
* be displayed in the Timeline.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTimeline = false;
|
||||
|
||||
/**
|
||||
* Whether this collector needs to display
|
||||
* content in a tab or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTabContent = false;
|
||||
|
||||
/**
|
||||
* Whether this collector needs to display
|
||||
* a label or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasLabel = false;
|
||||
|
||||
/**
|
||||
* Whether this collector has data that
|
||||
* should be shown in the Vars tab.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasVarData = false;
|
||||
|
||||
/**
|
||||
* The 'title' of this Collector.
|
||||
* Used to name things in the toolbar HTML.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = '';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets the Collector's title.
|
||||
*
|
||||
* @param boolean $safe
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(bool $safe = false): string
|
||||
{
|
||||
if ($safe)
|
||||
{
|
||||
return str_replace(' ', '-', strtolower($this->title));
|
||||
}
|
||||
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns any information that should be shown next to the title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitleDetails(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Does this collector need it's own tab?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasTabContent(): bool
|
||||
{
|
||||
return (bool) $this->hasTabContent;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Does this collector have a label?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasLabel(): bool
|
||||
{
|
||||
return (bool) $this->hasLabel;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Does this collector have information for the timeline?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasTimelineData(): bool
|
||||
{
|
||||
return (bool) $this->hasTimeline;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Grabs the data for the timeline, properly formatted,
|
||||
* or returns an empty array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function timelineData(): array
|
||||
{
|
||||
if (! $this->hasTimeline)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->formatTimelineData();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Does this Collector have data that should be shown in the
|
||||
* 'Vars' tab?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasVarData(): bool
|
||||
{
|
||||
return (bool) $this->hasVarData;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets a collection of data that should be shown in the 'Vars' tab.
|
||||
* The format is an array of sections, each with their own array
|
||||
* of key/value pairs:
|
||||
*
|
||||
* $data = [
|
||||
* 'section 1' => [
|
||||
* 'foo' => 'bar,
|
||||
* 'bar' => 'baz'
|
||||
* ],
|
||||
* 'section 2' => [
|
||||
* 'foo' => 'bar,
|
||||
* 'bar' => 'baz'
|
||||
* ],
|
||||
* ];
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getVarData()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Child classes should implement this to return the timeline data
|
||||
* formatted for correct usage.
|
||||
*
|
||||
* Timeline data should be formatted into arrays that look like:
|
||||
*
|
||||
* [
|
||||
* 'name' => 'Database::Query',
|
||||
* 'component' => 'Database',
|
||||
* 'start' => 10 // milliseconds
|
||||
* 'duration' => 15 // milliseconds
|
||||
* ]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function formatTimelineData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the data of this collector to be formatted in the toolbar
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public function display()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Clean Path
|
||||
*
|
||||
* This makes nicer looking paths for the error output.
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function cleanPath(string $file): string
|
||||
{
|
||||
if (strpos($file, APPPATH) === 0)
|
||||
{
|
||||
$file = 'APPPATH/' . substr($file, strlen(APPPATH));
|
||||
}
|
||||
elseif (strpos($file, SYSTEMPATH) === 0)
|
||||
{
|
||||
$file = 'SYSTEMPATH/' . substr($file, strlen(SYSTEMPATH));
|
||||
}
|
||||
elseif (strpos($file, FCPATH) === 0)
|
||||
{
|
||||
$file = 'FCPATH/' . substr($file, strlen(FCPATH));
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "badge" value for the button.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getBadgeValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this collector have any data collected?
|
||||
*
|
||||
* If not, then the toolbar button won't get shown.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTML to display the icon. Should either
|
||||
* be SVG, or a base-64 encoded.
|
||||
*
|
||||
* Recommended dimensions are 24px x 24px
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function icon(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return settings as an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAsArray(): array
|
||||
{
|
||||
return [
|
||||
'title' => $this->getTitle(),
|
||||
'titleSafe' => $this->getTitle(true),
|
||||
'titleDetails' => $this->getTitleDetails(),
|
||||
'display' => $this->display(),
|
||||
'badgeValue' => $this->getBadgeValue(),
|
||||
'isEmpty' => $this->isEmpty(),
|
||||
'hasTabContent' => $this->hasTabContent(),
|
||||
'hasLabel' => $this->hasLabel(),
|
||||
'icon' => $this->icon(),
|
||||
'hasTimelineData' => $this->hasTimelineData(),
|
||||
'timelineData' => $this->timelineData(),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
71
system/Debug/Toolbar/Collectors/Config.php
Normal file
71
system/Debug/Toolbar/Collectors/Config.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug\Toolbar\Collectors;
|
||||
|
||||
use Config\App;
|
||||
use Config\Services;
|
||||
use CodeIgniter\CodeIgniter;
|
||||
|
||||
/**
|
||||
* Debug toolbar configuration
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
/**
|
||||
* Return toolbar config values as an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function display(): array
|
||||
{
|
||||
$config = config(App::class);
|
||||
|
||||
return [
|
||||
'ciVersion' => CodeIgniter::CI_VERSION,
|
||||
'phpVersion' => phpversion(),
|
||||
'phpSAPI' => php_sapi_name(),
|
||||
'environment' => ENVIRONMENT,
|
||||
'baseURL' => $config->baseURL,
|
||||
'timezone' => app_timezone(),
|
||||
'locale' => Services::request()->getLocale(),
|
||||
'cspEnabled' => $config->CSPEnabled,
|
||||
];
|
||||
}
|
||||
}
|
||||
276
system/Debug/Toolbar/Collectors/Database.php
Normal file
276
system/Debug/Toolbar/Collectors/Database.php
Normal file
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug\Toolbar\Collectors;
|
||||
|
||||
use CodeIgniter\Database\Query;
|
||||
|
||||
/**
|
||||
* Collector for the Database tab of the Debug Toolbar.
|
||||
*/
|
||||
class Database extends BaseCollector
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether this collector has timeline data.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTimeline = true;
|
||||
|
||||
/**
|
||||
* Whether this collector should display its own tab.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTabContent = true;
|
||||
|
||||
/**
|
||||
* Whether this collector has data for the Vars tab.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasVarData = false;
|
||||
|
||||
/**
|
||||
* The name used to reference this collector in the toolbar.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'Database';
|
||||
|
||||
/**
|
||||
* Array of database connections.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $connections;
|
||||
|
||||
/**
|
||||
* The query instances that have been collected
|
||||
* through the DBQuery Event.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $queries = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->connections = \Config\Database::getConnections();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The static method used during Events to collect
|
||||
* data.
|
||||
*
|
||||
* @param \CodeIgniter\Database\Query $query
|
||||
*
|
||||
* @internal param $ array \CodeIgniter\Database\Query
|
||||
*/
|
||||
public static function collect(Query $query)
|
||||
{
|
||||
$config = config('Toolbar');
|
||||
|
||||
// Provide default in case it's not set
|
||||
$max = $config->maxQueries ?: 100;
|
||||
|
||||
if (count(static::$queries) < $max)
|
||||
{
|
||||
static::$queries[] = $query;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns timeline data formatted for the toolbar.
|
||||
*
|
||||
* @return array The formatted data or an empty array.
|
||||
*/
|
||||
protected function formatTimelineData(): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->connections as $alias => $connection)
|
||||
{
|
||||
// Connection Time
|
||||
$data[] = [
|
||||
'name' => 'Connecting to Database: "' . $alias . '"',
|
||||
'component' => 'Database',
|
||||
'start' => $connection->getConnectStart(),
|
||||
'duration' => $connection->getConnectDuration(),
|
||||
];
|
||||
}
|
||||
|
||||
foreach (static::$queries as $query)
|
||||
{
|
||||
$data[] = [
|
||||
'name' => 'Query',
|
||||
'component' => 'Database',
|
||||
'start' => $query->getStartTime(true),
|
||||
'duration' => $query->getDuration(),
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the data of this collector to be formatted in the toolbar
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function display(): array
|
||||
{
|
||||
// Key words we want bolded
|
||||
$highlight = [
|
||||
'SELECT',
|
||||
'DISTINCT',
|
||||
'FROM',
|
||||
'WHERE',
|
||||
'AND',
|
||||
'LEFT JOIN',
|
||||
'ORDER BY',
|
||||
'GROUP BY',
|
||||
'LIMIT',
|
||||
'INSERT',
|
||||
'INTO',
|
||||
'VALUES',
|
||||
'UPDATE',
|
||||
'OR ',
|
||||
'HAVING',
|
||||
'OFFSET',
|
||||
'NOT IN',
|
||||
'IN',
|
||||
'LIKE',
|
||||
'NOT LIKE',
|
||||
'COUNT',
|
||||
'MAX',
|
||||
'MIN',
|
||||
'ON',
|
||||
'AS',
|
||||
'AVG',
|
||||
'SUM',
|
||||
'(',
|
||||
')',
|
||||
];
|
||||
|
||||
$data = [
|
||||
'queries' => [],
|
||||
];
|
||||
|
||||
foreach (static::$queries as $query)
|
||||
{
|
||||
$sql = $query->getQuery();
|
||||
|
||||
foreach ($highlight as $term)
|
||||
{
|
||||
$sql = str_replace($term, "<strong>{$term}</strong>", $sql);
|
||||
}
|
||||
|
||||
$data['queries'][] = [
|
||||
'duration' => ($query->getDuration(5) * 1000) . ' ms',
|
||||
'sql' => $sql,
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets the "badge" value for the button.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getBadgeValue(): int
|
||||
{
|
||||
return count(static::$queries);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Information to be displayed next to the title.
|
||||
*
|
||||
* @return string The number of queries (in parentheses) or an empty string.
|
||||
*/
|
||||
public function getTitleDetails(): string
|
||||
{
|
||||
return '(' . count(static::$queries) . ' Queries across ' . ($countConnection = count($this->connections)) . ' Connection' .
|
||||
($countConnection > 1 ? 's' : '') . ')';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Does this collector have any data collected?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty(static::$queries);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Display the icon.
|
||||
*
|
||||
* Icon from https://icons8.com - 1em package
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function icon(): string
|
||||
{
|
||||
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADMSURBVEhLY6A3YExLSwsA4nIycQDIDIhRWEBqamo/UNF/SjDQjF6ocZgAKPkRiFeEhoYyQ4WIBiA9QAuWAPEHqBAmgLqgHcolGQD1V4DMgHIxwbCxYD+QBqcKINseKo6eWrBioPrtQBq/BcgY5ht0cUIYbBg2AJKkRxCNWkDQgtFUNJwtABr+F6igE8olGQD114HMgHIxAVDyAhA/AlpSA8RYUwoeXAPVex5qHCbIyMgwBCkAuQJIY00huDBUz/mUlBQDqHGjgBjAwAAACexpph6oHSQAAAAASUVORK5CYII=';
|
||||
}
|
||||
|
||||
}
|
||||
187
system/Debug/Toolbar/Collectors/Events.php
Normal file
187
system/Debug/Toolbar/Collectors/Events.php
Normal file
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug\Toolbar\Collectors;
|
||||
|
||||
use CodeIgniter\Config\Services;
|
||||
use CodeIgniter\View\RendererInterface;
|
||||
|
||||
/**
|
||||
* Views collector
|
||||
*/
|
||||
class Events extends BaseCollector
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether this collector has data that can
|
||||
* be displayed in the Timeline.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTimeline = false;
|
||||
|
||||
/**
|
||||
* Whether this collector needs to display
|
||||
* content in a tab or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTabContent = true;
|
||||
|
||||
/**
|
||||
* Whether this collector has data that
|
||||
* should be shown in the Vars tab.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasVarData = false;
|
||||
|
||||
/**
|
||||
* The 'title' of this Collector.
|
||||
* Used to name things in the toolbar HTML.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'Events';
|
||||
|
||||
/**
|
||||
* Instance of the Renderer service
|
||||
*
|
||||
* @var RendererInterface
|
||||
*/
|
||||
protected $viewer;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->viewer = Services::renderer(null, true);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Child classes should implement this to return the timeline data
|
||||
* formatted for correct usage.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function formatTimelineData(): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
$rows = $this->viewer->getPerformanceData();
|
||||
|
||||
foreach ($rows as $name => $info)
|
||||
{
|
||||
$data[] = [
|
||||
'name' => 'View: ' . $info['view'],
|
||||
'component' => 'Views',
|
||||
'start' => $info['start'],
|
||||
'duration' => $info['end'] - $info['start'],
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the data of this collector to be formatted in the toolbar
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function display(): array
|
||||
{
|
||||
$data = [
|
||||
'events' => [],
|
||||
];
|
||||
|
||||
foreach (\CodeIgniter\Events\Events::getPerformanceLogs() as $row)
|
||||
{
|
||||
$key = $row['event'];
|
||||
|
||||
if (! array_key_exists($key, $data['events']))
|
||||
{
|
||||
$data['events'][$key] = [
|
||||
'event' => $key,
|
||||
'duration' => number_format(($row['end'] - $row['start']) * 1000, 2),
|
||||
'count' => 1,
|
||||
];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$data['events'][$key]['duration'] += number_format(($row['end'] - $row['start']) * 1000, 2);
|
||||
$data['events'][$key]['count']++;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets the "badge" value for the button.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getBadgeValue(): int
|
||||
{
|
||||
return count(\CodeIgniter\Events\Events::getPerformanceLogs());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Display the icon.
|
||||
*
|
||||
* Icon from https://icons8.com - 1em package
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function icon(): string
|
||||
{
|
||||
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEASURBVEhL7ZXNDcIwDIVTsRBH1uDQDdquUA6IM1xgCA6MwJUN2hk6AQzAz0vl0ETUxC5VT3zSU5w81/mRMGZysixbFEVR0jSKNt8geQU9aRpFmp/keX6AbjZ5oB74vsaN5lSzA4tLSjpBFxsjeSuRy4d2mDdQTWU7YLbXTNN05mKyovj5KL6B7q3hoy3KwdZxBlT+Ipz+jPHrBqOIynZgcZonoukb/0ckiTHqNvDXtXEAaygRbaB9FvUTjRUHsIYS0QaSp+Dw6wT4hiTmYHOcYZsdLQ2CbXa4ftuuYR4x9vYZgdb4vsFYUdmABMYeukK9/SUme3KMFQ77+Yfzh8eYF8+orDuDWU5LAAAAAElFTkSuQmCC';
|
||||
}
|
||||
}
|
||||
151
system/Debug/Toolbar/Collectors/Files.php
Normal file
151
system/Debug/Toolbar/Collectors/Files.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug\Toolbar\Collectors;
|
||||
|
||||
/**
|
||||
* Files collector
|
||||
*/
|
||||
class Files extends BaseCollector
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether this collector has data that can
|
||||
* be displayed in the Timeline.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTimeline = false;
|
||||
|
||||
/**
|
||||
* Whether this collector needs to display
|
||||
* content in a tab or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTabContent = true;
|
||||
|
||||
/**
|
||||
* The 'title' of this Collector.
|
||||
* Used to name things in the toolbar HTML.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'Files';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns any information that should be shown next to the title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitleDetails(): string
|
||||
{
|
||||
return '( ' . (int) count(get_included_files()) . ' )';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the data of this collector to be formatted in the toolbar
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function display(): array
|
||||
{
|
||||
$rawFiles = get_included_files();
|
||||
$coreFiles = [];
|
||||
$userFiles = [];
|
||||
|
||||
foreach ($rawFiles as $file)
|
||||
{
|
||||
$path = $this->cleanPath($file);
|
||||
|
||||
if (strpos($path, 'SYSTEMPATH') !== false)
|
||||
{
|
||||
$coreFiles[] = [
|
||||
'name' => basename($file),
|
||||
'path' => $path,
|
||||
];
|
||||
}
|
||||
else
|
||||
{
|
||||
$userFiles[] = [
|
||||
'name' => basename($file),
|
||||
'path' => $path,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
sort($userFiles);
|
||||
sort($coreFiles);
|
||||
|
||||
return [
|
||||
'coreFiles' => $coreFiles,
|
||||
'userFiles' => $userFiles,
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Displays the number of included files as a badge in the tab button.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getBadgeValue(): int
|
||||
{
|
||||
return count(get_included_files());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Display the icon.
|
||||
*
|
||||
* Icon from https://icons8.com - 1em package
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function icon(): string
|
||||
{
|
||||
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGBSURBVEhL7ZQ9S8NQGIVTBQUncfMfCO4uLgoKbuKQOWg+OkXERRE1IAXrIHbVDrqIDuLiJgj+gro7S3dnpfq88b1FMTE3VZx64HBzzvvZWxKnj15QCcPwCD5HUfSWR+JtzgmtsUcQBEva5IIm9SwSu+95CAWbUuy67qBa32ByZEDpIaZYZSZMjjQuPcQUq8yEyYEb8FSerYeQVGbAFzJkX1PyQWLhgCz0BxTCekC1Wp0hsa6yokzhed4oje6Iz6rlJEkyIKfUEFtITVtQdAibn5rMyaYsMS+a5wTv8qeXMhcU16QZbKgl3hbs+L4/pnpdc87MElZgq10p5DxGdq8I7xrvUWUKvG3NbSK7ubngYzdJwSsF7TiOh9VOgfcEz1UayNe3JUPM1RWC5GXYgTfc75B4NBmXJnAtTfpABX0iPvEd9ezALwkplCFXcr9styiNOKc1RRZpaPM9tcqBwlWzGY1qPL9wjqRBgF5BH6j8HWh2S7MHlX8PrmbK+k/8PzjOOzx1D3i1pKTTAAAAAElFTkSuQmCC';
|
||||
}
|
||||
}
|
||||
183
system/Debug/Toolbar/Collectors/History.php
Normal file
183
system/Debug/Toolbar/Collectors/History.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug\Toolbar\Collectors;
|
||||
|
||||
/**
|
||||
* History collector
|
||||
*/
|
||||
class History extends BaseCollector
|
||||
{
|
||||
/**
|
||||
* Whether this collector has data that can
|
||||
* be displayed in the Timeline.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTimeline = false;
|
||||
|
||||
/**
|
||||
* Whether this collector needs to display
|
||||
* content in a tab or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTabContent = true;
|
||||
|
||||
/**
|
||||
* Whether this collector needs to display
|
||||
* a label or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasLabel = true;
|
||||
|
||||
/**
|
||||
* The 'title' of this Collector.
|
||||
* Used to name things in the toolbar HTML.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'History';
|
||||
|
||||
/**
|
||||
* @var array History files
|
||||
*/
|
||||
protected $files = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Specify time limit & file count for debug history.
|
||||
*
|
||||
* @param integer $current Current history time
|
||||
* @param integer $limit Max history files
|
||||
*/
|
||||
public function setFiles(int $current, int $limit = 20)
|
||||
{
|
||||
$filenames = glob(WRITEPATH . 'debugbar/debugbar_*.json');
|
||||
|
||||
$files = [];
|
||||
$counter = 0;
|
||||
|
||||
foreach (array_reverse($filenames) as $filename)
|
||||
{
|
||||
$counter++;
|
||||
|
||||
// Oldest files will be deleted
|
||||
if ($limit >= 0 && $counter > $limit)
|
||||
{
|
||||
unlink($filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the contents of this specific history request
|
||||
$contents = file_get_contents($filename);
|
||||
|
||||
$contents = @json_decode($contents);
|
||||
if (json_last_error() === JSON_ERROR_NONE)
|
||||
{
|
||||
preg_match_all('/\d+/', $filename, $time);
|
||||
$time = (int)end($time[0]);
|
||||
|
||||
// Debugbar files shown in History Collector
|
||||
$files[] = [
|
||||
'time' => $time,
|
||||
'datetime' => date('Y-m-d H:i:s', $time),
|
||||
'active' => $time === $current,
|
||||
'status' => $contents->vars->response->statusCode,
|
||||
'method' => $contents->method,
|
||||
'url' => $contents->url,
|
||||
'isAJAX' => $contents->isAJAX ? 'Yes' : 'No',
|
||||
'contentType' => $contents->vars->response->contentType,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the data of this collector to be formatted in the toolbar
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function display(): array
|
||||
{
|
||||
return ['files' => $this->files];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Displays the number of included files as a badge in the tab button.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getBadgeValue(): int
|
||||
{
|
||||
return count($this->files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no history files.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty($this->files);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Display the icon.
|
||||
*
|
||||
* Icon from https://icons8.com - 1em package
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function icon(): string
|
||||
{
|
||||
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJySURBVEhL3ZU7aJNhGIVTpV6i4qCIgkIHxcXLErS4FBwUFNwiCKGhuTYJGaIgnRoo4qRu6iCiiIuIXXTTIkIpuqoFwaGgonUQlC5KafU5ycmNP0lTdPLA4fu+8573/a4/f6hXpFKpwUwmc9fDfweKbk+n07fgEv33TLSbtt/hvwNFT1PsG/zdTE0Gp+GFfD6/2fbVIxqNrqPIRbjg4t/hY8aztcngfDabHXbKyiiXy2vcrcPH8oDCry2FKDrA+Ar6L01E/ypyXzXaARjDGGcoeNxSDZXE0dHRA5VRE5LJ5CFy5jzJuOX2wHRHRnjbklZ6isQ3tIctBaAd4vlK3jLtkOVWqABBXd47jGHLmjTmSScttQV5J+SjfcUweFQEbsjAas5aqoCLXutJl7vtQsAzpRowYqkBinyCC8Vicb2lOih8zoldd0F8RD7qTFiqAnGrAy8stUAvi/hbqDM+YzkAFrLPdR5ZqoLXsd+Bh5YCIH7JniVdquUWxOPxDfboHhrI5XJ7HHhiqQXox+APe/Qk64+gGYVCYZs8cMpSFQj9JOoFzVqqo7k4HIvFYpscCoAjOmLffUsNUGRaQUwDlmofUa34ecsdgXdcXo4wbakBgiUFafXJV8A4DJ/2UrxUKm3E95H8RbjLcgOJRGILhnmCP+FBy5XvwN2uIPcy1AJvWgqC4xm2aU4Xb3lF4I+Tpyf8hRe5w3J7YLymSeA8Z3nSclv4WLRyFdfOjzrUFX0klJUEtZtntCNc+F69cz/FiDzEPtjzmcUMOr83kDQEX6pAJxJfpL3OX22n01YN7SZCoQnaSdoZ+Jz+PZihH3wt/xlCoT9M6nEtmRSPCQAAAABJRU5ErkJggg==';
|
||||
}
|
||||
}
|
||||
139
system/Debug/Toolbar/Collectors/Logs.php
Normal file
139
system/Debug/Toolbar/Collectors/Logs.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug\Toolbar\Collectors;
|
||||
|
||||
use CodeIgniter\Config\Services;
|
||||
|
||||
/**
|
||||
* Loags collector
|
||||
*/
|
||||
class Logs extends BaseCollector
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether this collector has data that can
|
||||
* be displayed in the Timeline.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTimeline = false;
|
||||
|
||||
/**
|
||||
* Whether this collector needs to display
|
||||
* content in a tab or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTabContent = true;
|
||||
|
||||
/**
|
||||
* The 'title' of this Collector.
|
||||
* Used to name things in the toolbar HTML.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'Logs';
|
||||
|
||||
/**
|
||||
* Our collected data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the data of this collector to be formatted in the toolbar
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function display(): array
|
||||
{
|
||||
return [
|
||||
'logs' => $this->collectLogs(),
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Does this collector actually have any data to display?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
$this->collectLogs();
|
||||
|
||||
return empty($this->data);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Display the icon.
|
||||
*
|
||||
* Icon from https://icons8.com - 1em package
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function icon(): string
|
||||
{
|
||||
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACYSURBVEhLYxgFJIHU1FSjtLS0i0D8AYj7gEKMEBkqAaAFF4D4ERCvAFrwH4gDoFIMKSkpFkB+OTEYqgUTACXfA/GqjIwMQyD9H2hRHlQKJFcBEiMGQ7VgAqCBvUgK32dmZspCpagGGNPT0/1BLqeF4bQHQJePpiIwhmrBBEADR1MRfgB0+WgqAmOoFkwANHA0FY0CUgEDAwCQ0PUpNB3kqwAAAABJRU5ErkJggg==';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Ensures the data has been collected.
|
||||
*/
|
||||
protected function collectLogs()
|
||||
{
|
||||
if (! is_null($this->data))
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
return $this->data = Services::logger(true)->logCache ?? [];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
||||
181
system/Debug/Toolbar/Collectors/Routes.php
Normal file
181
system/Debug/Toolbar/Collectors/Routes.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug\Toolbar\Collectors;
|
||||
|
||||
use CodeIgniter\Config\Services;
|
||||
|
||||
/**
|
||||
* Routes collector
|
||||
*/
|
||||
class Routes extends BaseCollector
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether this collector has data that can
|
||||
* be displayed in the Timeline.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTimeline = false;
|
||||
|
||||
/**
|
||||
* Whether this collector needs to display
|
||||
* content in a tab or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTabContent = true;
|
||||
|
||||
/**
|
||||
* The 'title' of this Collector.
|
||||
* Used to name things in the toolbar HTML.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'Routes';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the data of this collector to be formatted in the toolbar
|
||||
*
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function display(): array
|
||||
{
|
||||
$rawRoutes = Services::routes(true);
|
||||
$router = Services::router(null, null, true);
|
||||
|
||||
/*
|
||||
* Matched Route
|
||||
*/
|
||||
$route = $router->getMatchedRoute();
|
||||
|
||||
// Get our parameters
|
||||
// Closure routes
|
||||
if (is_callable($router->controllerName()))
|
||||
{
|
||||
$method = new \ReflectionFunction($router->controllerName());
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
$method = new \ReflectionMethod($router->controllerName(), $router->methodName());
|
||||
}
|
||||
catch (\ReflectionException $e)
|
||||
{
|
||||
// If we're here, the method doesn't exist
|
||||
// and is likely calculated in _remap.
|
||||
$method = new \ReflectionMethod($router->controllerName(), '_remap');
|
||||
}
|
||||
}
|
||||
|
||||
$rawParams = $method->getParameters();
|
||||
|
||||
$params = [];
|
||||
foreach ($rawParams as $key => $param)
|
||||
{
|
||||
$params[] = [
|
||||
'name' => $param->getName(),
|
||||
'value' => $router->params()[$key] ??
|
||||
'<empty> | default: ' . var_export($param->isDefaultValueAvailable() ? $param->getDefaultValue() : null, true),
|
||||
];
|
||||
}
|
||||
|
||||
$matchedRoute = [
|
||||
[
|
||||
'directory' => $router->directory(),
|
||||
'controller' => $router->controllerName(),
|
||||
'method' => $router->methodName(),
|
||||
'paramCount' => count($router->params()),
|
||||
'truePCount' => count($params),
|
||||
'params' => $params ?? [],
|
||||
],
|
||||
];
|
||||
|
||||
/*
|
||||
* Defined Routes
|
||||
*/
|
||||
$rawRoutes = $rawRoutes->getRoutes();
|
||||
$routes = [];
|
||||
|
||||
foreach ($rawRoutes as $from => $to)
|
||||
{
|
||||
$routes[] = [
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'matchedRoute' => $matchedRoute,
|
||||
'routes' => $routes,
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a count of all the routes in the system.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getBadgeValue(): int
|
||||
{
|
||||
$rawRoutes = Services::routes(true);
|
||||
|
||||
return count($rawRoutes->getRoutes());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Display the icon.
|
||||
*
|
||||
* Icon from https://icons8.com - 1em package
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function icon(): string
|
||||
{
|
||||
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFDSURBVEhL7ZRNSsNQFIUjVXSiOFEcuQIHDpzpxC0IGYeE/BEInbWlCHEDLsSiuANdhKDjgm6ggtSJ+l25ldrmmTwIgtgDh/t37r1J+16cX0dRFMtpmu5pWAkrvYjjOB7AETzStBFW+inxu3KUJMmhludQpoflS1zXban4LYqiO224h6VLTHr8Z+z8EpIHFF9gG78nDVmW7UgTHKjsCyY98QP+pcq+g8Ku2s8G8X3f3/I8b038WZTp+bO38zxfFd+I6YY6sNUvFlSDk9CRhiAI1jX1I9Cfw7GG1UB8LAuwbU0ZwQnbRDeEN5qqBxZMLtE1ti9LtbREnMIuOXnyIf5rGIb7Wq8HmlZgwYBH7ORTcKH5E4mpjeGt9fBZcHE2GCQ3Vt7oTNPNg+FXLHnSsHkw/FR+Gg2bB8Ptzrst/v6C/wrH+QB+duli6MYJdQAAAABJRU5ErkJggg==';
|
||||
}
|
||||
}
|
||||
107
system/Debug/Toolbar/Collectors/Timers.php
Normal file
107
system/Debug/Toolbar/Collectors/Timers.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug\Toolbar\Collectors;
|
||||
|
||||
use CodeIgniter\Config\Services;
|
||||
|
||||
/**
|
||||
* Timers collector
|
||||
*/
|
||||
class Timers extends BaseCollector
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether this collector has data that can
|
||||
* be displayed in the Timeline.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTimeline = true;
|
||||
|
||||
/**
|
||||
* Whether this collector needs to display
|
||||
* content in a tab or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTabContent = false;
|
||||
|
||||
/**
|
||||
* The 'title' of this Collector.
|
||||
* Used to name things in the toolbar HTML.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'Timers';
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Child classes should implement this to return the timeline data
|
||||
* formatted for correct usage.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function formatTimelineData(): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
$benchmark = Services::timer(true);
|
||||
$rows = $benchmark->getTimers(6);
|
||||
|
||||
foreach ($rows as $name => $info)
|
||||
{
|
||||
if ($name === 'total_execution')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[] = [
|
||||
'name' => ucwords(str_replace('_', ' ', $name)),
|
||||
'component' => 'Timer',
|
||||
'start' => $info['start'],
|
||||
'duration' => $info['end'] - $info['start'],
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
192
system/Debug/Toolbar/Collectors/Views.php
Normal file
192
system/Debug/Toolbar/Collectors/Views.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
* An open source application development framework for PHP
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
* Copyright (c) 2019 CodeIgniter Foundation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2019 CodeIgniter Foundation
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 4.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Debug\Toolbar\Collectors;
|
||||
|
||||
use CodeIgniter\Config\Services;
|
||||
use CodeIgniter\View\RendererInterface;
|
||||
|
||||
/**
|
||||
* Views collector
|
||||
*/
|
||||
class Views extends BaseCollector
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether this collector has data that can
|
||||
* be displayed in the Timeline.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTimeline = true;
|
||||
|
||||
/**
|
||||
* Whether this collector needs to display
|
||||
* content in a tab or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasTabContent = false;
|
||||
|
||||
/**
|
||||
* Whether this collector needs to display
|
||||
* a label or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasLabel = true;
|
||||
|
||||
/**
|
||||
* Whether this collector has data that
|
||||
* should be shown in the Vars tab.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $hasVarData = true;
|
||||
|
||||
/**
|
||||
* The 'title' of this Collector.
|
||||
* Used to name things in the toolbar HTML.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'Views';
|
||||
|
||||
/**
|
||||
* Instance of the Renderer service
|
||||
*
|
||||
* @var RendererInterface
|
||||
*/
|
||||
protected $viewer;
|
||||
|
||||
/**
|
||||
* Views counter
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $views = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->viewer = Services::renderer();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Child classes should implement this to return the timeline data
|
||||
* formatted for correct usage.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function formatTimelineData(): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
$rows = $this->viewer->getPerformanceData();
|
||||
|
||||
foreach ($rows as $name => $info)
|
||||
{
|
||||
$data[] = [
|
||||
'name' => 'View: ' . $info['view'],
|
||||
'component' => 'Views',
|
||||
'start' => $info['start'],
|
||||
'duration' => $info['end'] - $info['start'],
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets a collection of data that should be shown in the 'Vars' tab.
|
||||
* The format is an array of sections, each with their own array
|
||||
* of key/value pairs:
|
||||
*
|
||||
* $data = [
|
||||
* 'section 1' => [
|
||||
* 'foo' => 'bar,
|
||||
* 'bar' => 'baz'
|
||||
* ],
|
||||
* 'section 2' => [
|
||||
* 'foo' => 'bar,
|
||||
* 'bar' => 'baz'
|
||||
* ],
|
||||
* ];
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getVarData(): array
|
||||
{
|
||||
return [
|
||||
'View Data' => $this->viewer->getData(),
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a count of all views.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getBadgeValue(): int
|
||||
{
|
||||
return count($this->viewer->getPerformanceData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the icon.
|
||||
*
|
||||
* Icon from https://icons8.com - 1em package
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function icon(): string
|
||||
{
|
||||
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADeSURBVEhL7ZSxDcIwEEWNYA0YgGmgyAaJLTcUaaBzQQEVjMEabBQxAdw53zTHiThEovGTfnE/9rsoRUxhKLOmaa6Uh7X2+UvguLCzVxN1XW9x4EYHzik033Hp3X0LO+DaQG8MDQcuq6qao4qkHuMgQggLvkPLjqh00ZgFDBacMJYFkuwFlH1mshdkZ5JPJERA9JpI6xNCBESvibQ+IURA9JpI6xNCBESvibQ+IURA9DTsuHTOrVFFxixgB/eUFlU8uKJ0eDBFOu/9EvoeKnlJS2/08Tc8NOwQ8sIfMeYFjqKDjdU2sp4AAAAASUVORK5CYII=';
|
||||
}
|
||||
}
|
||||
48
system/Debug/Toolbar/Views/_config.tpl
Normal file
48
system/Debug/Toolbar/Views/_config.tpl
Normal file
@@ -0,0 +1,48 @@
|
||||
<p class="debug-bar-alignRight">
|
||||
<a href="https://codeigniter4.github.io/CodeIgniter4/index.html" target="_blank" >Read the CodeIgniter docs...</a>
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>CodeIgniter Version:</td>
|
||||
<td>{ ciVersion }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PHP Version:</td>
|
||||
<td>{ phpVersion }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PHP SAPI:</td>
|
||||
<td>{ phpSAPI }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Environment:</td>
|
||||
<td>{ environment }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Base URL:</td>
|
||||
<td>
|
||||
{ if $baseURL == '' }
|
||||
<div class="warning">
|
||||
The $baseURL should always be set manually to prevent possible URL personification from external parties.
|
||||
</div>
|
||||
{ else }
|
||||
{ baseURL }
|
||||
{ endif }
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TimeZone:</td>
|
||||
<td>{ timezone }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Locale:</td>
|
||||
<td>{ locale }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Content Security Policy Enabled:</td>
|
||||
<td>{ if $cspEnabled } Yes { else } No { endif }</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
16
system/Debug/Toolbar/Views/_database.tpl
Normal file
16
system/Debug/Toolbar/Views/_database.tpl
Normal file
@@ -0,0 +1,16 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="debug-bar-width6r">Time</th>
|
||||
<th>Query String</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{queries}
|
||||
<tr>
|
||||
<td class="narrow">{duration}</td>
|
||||
<td>{! sql !}</td>
|
||||
</tr>
|
||||
{/queries}
|
||||
</tbody>
|
||||
</table>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user