mirror of
https://github.com/codeigniter4/CodeIgniter4.git
synced 2025-02-20 11:44:28 +08:00
Merge branch 'develop' into refactor/email
This commit is contained in:
commit
417b1f9d73
@ -1,7 +1,6 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
- nightly
|
||||
@ -9,7 +8,6 @@ php:
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- php: 7.3
|
||||
- php: nightly
|
||||
|
||||
global:
|
||||
|
@ -5,6 +5,7 @@
|
||||
<br>
|
||||
|
||||
## What is CodeIgniter?
|
||||
|
||||
CodeIgniter is a PHP full-stack web framework that is light, fast, flexible, and secure.
|
||||
More information can be found at the [official site](http://codeigniter.com).
|
||||
|
||||
@ -35,6 +36,7 @@ framework are exposed.
|
||||
The user guide updating and deployment is a bit awkward at the moment, but we are working on it!
|
||||
|
||||
## Repository Management
|
||||
|
||||
We use Github issues to track **BUGS** and to track approved **DEVELOPMENT** work packages.
|
||||
We use our [forum](http://forum.codeigniter.com) to provide SUPPORT and to discuss
|
||||
FEATURE REQUESTS.
|
||||
@ -57,6 +59,7 @@ Remember that some components that were part of CodeIgniter 3 are being moved
|
||||
to optional packages, with their own repository.
|
||||
|
||||
## Contributing
|
||||
|
||||
We **are** accepting contributions from the community, specifically those identified as part of phase 2.
|
||||
|
||||
We will try to manage the process somewhat, by adding a "Help wanted" label to those that we are
|
||||
@ -68,7 +71,9 @@ We are not looking for out-of-scope contributions, only those that would be cons
|
||||
Please read the [*Contributing to CodeIgniter*](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing.md) section in the user guide
|
||||
|
||||
## Server Requirements
|
||||
PHP version 7.1 or higher is required, with the following extensions installed:
|
||||
|
||||
PHP version 7.2 or higher is required, with the following extensions installed:
|
||||
|
||||
|
||||
- [intl](http://php.net/manual/en/intl.requirements.php)
|
||||
- [libcurl](http://php.net/manual/en/curl.requirements.php) if you plan to use the HTTP\CURLRequest library
|
||||
@ -81,4 +86,5 @@ Additionally, make sure that the following extensions are enabled in your PHP:
|
||||
- xml (enabled by default - don't turn it off)
|
||||
|
||||
## Running CodeIgniter Tests
|
||||
|
||||
Information on running CodeIgniter test suite can be found in the [README.md](tests/README.md) file in the tests directory.
|
||||
|
@ -1,8 +1,8 @@
|
||||
#CodeIgniter 4 Admin
|
||||
# CodeIgniter 4 Admin
|
||||
|
||||
This folder contains tools or docs useful for project maintainers.
|
||||
|
||||
##Repositories inside https://github.com/codeigniter4
|
||||
## Repositories inside https://github.com/codeigniter4
|
||||
|
||||
- **CodeIgniter4** is the main development repository.
|
||||
It supports issues and pull requests, and has a rule to enforce GPG-signed commits.
|
||||
@ -35,7 +35,7 @@ This folder contains tools or docs useful for project maintainers.
|
||||
It is community-maintained, and accepts issues and pull requests.
|
||||
It could be downloaded, forked or composer-installed.
|
||||
|
||||
##Contributor Scripts
|
||||
## Contributor Scripts
|
||||
|
||||
- **setup.sh** installs a git pre-commit hook into a contributor's
|
||||
local clone of their fork of the `CodeIgniter4` repository.
|
||||
@ -43,7 +43,7 @@ This folder contains tools or docs useful for project maintainers.
|
||||
to be added as part of a git commit, ensuring that they conform to the
|
||||
framework coding style standards, and automatically fixing what can be.
|
||||
|
||||
##Maintainer Scripts
|
||||
## Maintainer Scripts
|
||||
|
||||
- **release-config** holds variables used for the maintainer & release building
|
||||
- **docbot** re-builds the user guide from the RST source for it,
|
||||
@ -51,7 +51,7 @@ This folder contains tools or docs useful for project maintainers.
|
||||
repository (if the user running it has maintainer rights on that repo).
|
||||
See the [writeup](./docbot.md).
|
||||
|
||||
##Release Building Scripts
|
||||
## Release Building Scripts
|
||||
|
||||
The release workflow is detailed in its own writeup; these are the main
|
||||
scripts used by the release manager:
|
||||
@ -79,7 +79,7 @@ scripts used by the release manager:
|
||||
Remember to be polite when running it.
|
||||
|
||||
|
||||
##Other Stuff
|
||||
## Other Stuff
|
||||
|
||||
- **release-notes.bb** is a boilerplate for forum announcements of a new release.
|
||||
It is marked up using [BBcode](https://en.wikipedia.org/wiki/BBCode).
|
||||
|
@ -1,6 +1,7 @@
|
||||
# CodeIgniter 4 Framework
|
||||
|
||||
## What is CodeIgniter?
|
||||
|
||||
CodeIgniter is a PHP full-stack web framework that is light, fast, flexible, and secure.
|
||||
More information can be found at the [official site](http://codeigniter.com).
|
||||
|
||||
@ -29,6 +30,7 @@ framework are exposed.
|
||||
The user guide updating and deployment is a bit awkward at the moment, but we are working on it!
|
||||
|
||||
## Repository Management
|
||||
|
||||
We use Github issues to track **BUGS** and to track approved **DEVELOPMENT** work packages.
|
||||
We use our [forum](http://forum.codeigniter.com) to provide SUPPORT and to discuss
|
||||
FEATURE REQUESTS.
|
||||
@ -51,12 +53,14 @@ Remember that some components that were part of CodeIgniter 3 are being moved
|
||||
to optional packages, with their own repository.
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions from the community.
|
||||
|
||||
Please read the [*Contributing to CodeIgniter*](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing.md) section in the development repository.
|
||||
|
||||
## Server Requirements
|
||||
PHP version 7.1 or higher is required, with the following extensions installed:
|
||||
|
||||
PHP version 7.2 or higher is required, with the following extensions installed:
|
||||
|
||||
- [intl](http://php.net/manual/en/intl.requirements.php)
|
||||
- [libcurl](http://php.net/manual/en/curl.requirements.php) if you plan to use the HTTP\CURLRequest library
|
||||
|
@ -5,7 +5,7 @@
|
||||
"homepage": "https://codeigniter.com",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"php": ">=7.2",
|
||||
"ext-curl": "*",
|
||||
"ext-intl": "*",
|
||||
"kint-php/kint": "^2.1",
|
||||
@ -14,7 +14,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"codeigniter4/codeigniter4-standard": "^1.0",
|
||||
"mikey179/vfsStream": "1.6.*",
|
||||
"mikey179/vfsstream": "1.6.*",
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"squizlabs/php_codesniffer": "^3.3"
|
||||
},
|
||||
|
@ -12,7 +12,7 @@ git checkout $branch
|
||||
echo -e "${BOLD}Build the framework distributable${NORMAL}"
|
||||
|
||||
echo -e "${BOLD}Copy the main files/folders...${NORMAL}"
|
||||
releasable='app public writable README.md contributing.md env license.txt spark'
|
||||
releasable='app public writable README.md contributing.md env license.txt spark tests/_support'
|
||||
for fff in $releasable ; do
|
||||
if [ -d "$fff" ] ; then
|
||||
rm -rf $fff
|
||||
@ -23,10 +23,6 @@ done
|
||||
echo -e "${BOLD}Override as needed...${NORMAL}"
|
||||
cp -rf ${CI_DIR}/admin/starter/* .
|
||||
|
||||
############# this can only happen after composer create-project/update
|
||||
#echo -e "${BOLD}Fix paths...${NORMAL}"
|
||||
#sed -i "/public $systemDirectory = 'system';/s/'system'/'vendor/codeigniter4/framework/system'/" app/Config/Paths.php
|
||||
|
||||
#---------------------------------------------------
|
||||
# And finally, get ready for merging
|
||||
echo -e "${BOLD}Assemble the pieces...${NORMAL}"
|
||||
|
@ -12,7 +12,7 @@ git checkout $branch
|
||||
echo -e "${BOLD}Build the framework distributable${NORMAL}"
|
||||
|
||||
echo -e "${BOLD}Copy the main files/folders...${NORMAL}"
|
||||
releasable='app docs public system writable contributing.md env license.txt spark'
|
||||
releasable='app docs public system writable contributing.md env license.txt spark tests/_support'
|
||||
for fff in $releasable ; do
|
||||
if [ -d "$fff" ] ; then
|
||||
rm -rf $fff
|
||||
|
@ -47,7 +47,7 @@ The user guide updating and deployment is a bit awkward at the moment, but we ar
|
||||
|
||||
## Server Requirements
|
||||
|
||||
PHP version 7.1 or higher is required, with the following extensions installed:
|
||||
PHP version 7.2 or higher is required, with the following extensions installed:
|
||||
|
||||
- [intl](http://php.net/manual/en/intl.requirements.php)
|
||||
- [libcurl](http://php.net/manual/en/curl.requirements.php) if you plan to use the HTTP\CURLRequest library
|
||||
|
77
admin/starter/app/Config/Paths.php
Normal file
77
admin/starter/app/Config/Paths.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php namespace Config;
|
||||
|
||||
/**
|
||||
* Holds the paths that are used by the system to
|
||||
* locate the main directories, app, system, etc.
|
||||
* Modifying these allows you to re-structure your application,
|
||||
* share a system folder between multiple applications, and more.
|
||||
*
|
||||
* All paths are relative to the project's root folder.
|
||||
*/
|
||||
|
||||
class Paths
|
||||
{
|
||||
/*
|
||||
*---------------------------------------------------------------
|
||||
* SYSTEM FOLDER NAME
|
||||
*---------------------------------------------------------------
|
||||
*
|
||||
* This variable must contain the name of your "system" folder.
|
||||
* Include the path if the folder is not in the same directory
|
||||
* as this file.
|
||||
*/
|
||||
public $systemDirectory = __DIR__ . '/../../vendor/codeigniter4/framework/system';
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------
|
||||
* APPLICATION FOLDER NAME
|
||||
*---------------------------------------------------------------
|
||||
*
|
||||
* If you want this front controller to use a different "app"
|
||||
* folder than the default one you can set its name here. The folder
|
||||
* can also be renamed or relocated anywhere on your getServer. If
|
||||
* you do, use a full getServer path. For more info please see the user guide:
|
||||
* http://codeigniter.com/user_guide/general/managing_apps.html
|
||||
*
|
||||
* NO TRAILING SLASH!
|
||||
*/
|
||||
public $appDirectory = __DIR__ . '/..';
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* WRITABLE DIRECTORY NAME
|
||||
* ---------------------------------------------------------------
|
||||
*
|
||||
* This variable must contain the name of your "writable" directory.
|
||||
* The writable directory allows you to group all directories that
|
||||
* need write permission to a single place that can be tucked away
|
||||
* for maximum security, keeping it out of the app and/or
|
||||
* system directories.
|
||||
*/
|
||||
public $writableDirectory = __DIR__ . '/../../writable';
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* TESTS DIRECTORY NAME
|
||||
* ---------------------------------------------------------------
|
||||
*
|
||||
* This variable must contain the name of your "tests" directory.
|
||||
* The writable directory allows you to group all directories that
|
||||
* need write permission to a single place that can be tucked away
|
||||
* for maximum security, keeping it out of the app and/or
|
||||
* system directories.
|
||||
*/
|
||||
public $testsDirectory = __DIR__ . '/../../tests';
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* VIEW DIRECTORY NAME
|
||||
* ---------------------------------------------------------------
|
||||
*
|
||||
* This variable must contain the name of the directory that
|
||||
* contains the view files used by your application. By
|
||||
* default this is in `app/Views`. This value
|
||||
* is used when no value is provided to `Services::renderer()`.
|
||||
*/
|
||||
public $viewDirectory = __DIR__ . '/../Views';
|
||||
}
|
@ -5,11 +5,11 @@
|
||||
"homepage": "https://codeigniter.com",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"php": ">=7.2",
|
||||
"codeigniter4/framework": "^4@alpha"
|
||||
},
|
||||
"require-dev": {
|
||||
"mikey179/vfsStream": "1.6.*",
|
||||
"mikey179/vfsstream": "1.6.*",
|
||||
"phpunit/phpunit": "^7.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
28
admin/starter/phpunit.xml.dist
Normal file
28
admin/starter/phpunit.xml.dist
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="tests/_support/_bootstrap.php"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
stopOnIncomplete="false"
|
||||
stopOnSkipped="false">
|
||||
<testsuites>
|
||||
<testsuite name="app">
|
||||
<directory>./tests</directory>
|
||||
<exclude>./tests/system</exclude>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./system</directory>
|
||||
<exclude>
|
||||
<directory>./system</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
</phpunit>
|
@ -1,6 +1,7 @@
|
||||
# CodeIgniter 4 User Guide
|
||||
|
||||
## What is CodeIgniter?
|
||||
|
||||
CodeIgniter is a PHP full-stack web framework that is light, fast, flexible, and secure.
|
||||
More information can be found at the [official site](http://codeigniter.com).
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
"homepage": "https://codeigniter.com",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"php": ">=7.2",
|
||||
"codeigniter4/framework": "^4"
|
||||
},
|
||||
"support": {
|
||||
|
@ -21,7 +21,7 @@ class App extends BaseConfig
|
||||
| environments.
|
||||
|
|
||||
*/
|
||||
public $baseURL = '';
|
||||
public $baseURL = 'http://localhost:8080';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -97,6 +97,7 @@ class Cache extends BaseConfig
|
||||
'password' => null,
|
||||
'port' => 6379,
|
||||
'timeout' => 0,
|
||||
'database' => 0,
|
||||
];
|
||||
|
||||
/*
|
||||
|
@ -7,9 +7,9 @@ class Filters extends BaseConfig
|
||||
// Makes reading things below nicer,
|
||||
// and simpler to change out script that's used.
|
||||
public $aliases = [
|
||||
'csrf' => \App\Filters\CSRF::class,
|
||||
'toolbar' => \App\Filters\DebugToolbar::class,
|
||||
'honeypot' => \App\Filters\Honeypot::class,
|
||||
'csrf' => \CodeIgniter\Filters\CSRF::class,
|
||||
'toolbar' => \CodeIgniter\Filters\DebugToolbar::class,
|
||||
'honeypot' => \CodeIgniter\Filters\Honeypot::class,
|
||||
];
|
||||
|
||||
// Always applied before every request
|
||||
|
@ -14,6 +14,16 @@ class Modules
|
||||
*/
|
||||
public $enabled = true;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Auto-Discovery Within Composer Packages Enabled?
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If true, then auto-discovery will happen across all namespaces loaded
|
||||
| by Composer, as well as the namespaces configured locally.
|
||||
*/
|
||||
public $discoverInComposer = true;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Auto-discover Rules
|
||||
|
@ -35,7 +35,7 @@ class Paths
|
||||
*
|
||||
* NO TRAILING SLASH!
|
||||
*/
|
||||
public $appDirectory = __DIR__ . '/../../app';
|
||||
public $appDirectory = __DIR__ . '/..';
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
@ -73,5 +73,5 @@ class Paths
|
||||
* default this is in `app/Views`. This value
|
||||
* is used when no value is provided to `Services::renderer()`.
|
||||
*/
|
||||
public $viewDirectory = __DIR__ . '/../../app/Views';
|
||||
public $viewDirectory = __DIR__ . '/../Views';
|
||||
}
|
||||
|
@ -1,64 +0,0 @@
|
||||
<?php namespace App\Filters;
|
||||
|
||||
use CodeIgniter\Filters\FilterInterface;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use CodeIgniter\Security\Exceptions\SecurityException;
|
||||
use Config\Services;
|
||||
|
||||
class CSRF implements FilterInterface
|
||||
{
|
||||
/**
|
||||
* Do whatever processing this filter needs to do.
|
||||
* By default it should not return anything during
|
||||
* normal execution. However, when an abnormal state
|
||||
* is found, it should return an instance of
|
||||
* CodeIgniter\HTTP\Response. If it does, script
|
||||
* execution will end and that Response will be
|
||||
* sent back to the client, allowing for error pages,
|
||||
* redirects, etc.
|
||||
*
|
||||
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function before(RequestInterface $request)
|
||||
{
|
||||
if ($request->isCLI())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$security = Services::security();
|
||||
|
||||
try
|
||||
{
|
||||
$security->CSRFVerify($request);
|
||||
}
|
||||
catch (SecurityException $e)
|
||||
{
|
||||
if (config('App')->CSRFRedirect && ! $request->isAJAX())
|
||||
{
|
||||
return redirect()->back()->with('error', $e->getMessage());
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* We don't have anything to do here.
|
||||
*
|
||||
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
|
||||
* @param ResponseInterface|\CodeIgniter\HTTP\Response $response
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function after(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
<?php namespace App\Filters;
|
||||
|
||||
use CodeIgniter\Filters\FilterInterface;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Config\Services;
|
||||
|
||||
class DebugToolbar implements FilterInterface
|
||||
{
|
||||
/**
|
||||
* We don't need to do anything here.
|
||||
*
|
||||
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function before(RequestInterface $request)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* If the debug flag is set (CI_DEBUG) then collect performance
|
||||
* and debug information and display it in a toolbar.
|
||||
*
|
||||
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
|
||||
* @param ResponseInterface|\CodeIgniter\HTTP\Response $response
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function after(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
Services::toolbar()->prepare();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use CodeIgniter\Filters\FilterInterface;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Config\Services;
|
||||
use CodeIgniter\Honeypot\Exceptions\HoneypotException;
|
||||
|
||||
class Honeypot implements FilterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Checks if Honeypot field is empty; if not
|
||||
* then the requester is a bot
|
||||
*
|
||||
* @param CodeIgniter\HTTP\RequestInterface $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function before(RequestInterface $request)
|
||||
{
|
||||
$honeypot = Services::honeypot(new \Config\Honeypot());
|
||||
if ($honeypot->hasContent($request))
|
||||
{
|
||||
throw HoneypotException::isBot();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a honypot to the current response.
|
||||
*
|
||||
* @param CodeIgniter\HTTP\RequestInterface $request
|
||||
* @param CodeIgniter\HTTP\ResponseInterface $response
|
||||
* @return mixed
|
||||
*/
|
||||
public function after(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$honeypot = Services::honeypot(new \Config\Honeypot());
|
||||
$honeypot->attachHoneypot($response);
|
||||
}
|
||||
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
<?php namespace App\Filters;
|
||||
|
||||
use CodeIgniter\Filters\FilterInterface;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Config\Services;
|
||||
|
||||
class Throttle implements FilterInterface
|
||||
{
|
||||
/**
|
||||
* This is a demo implementation of using the Throttler class
|
||||
* to implement rate limiting for your application.
|
||||
*
|
||||
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function before(RequestInterface $request)
|
||||
{
|
||||
$throttler = Services::throttler();
|
||||
|
||||
// Restrict an IP address to no more
|
||||
// than 1 request per second across the
|
||||
// entire site.
|
||||
if ($throttler->check($request->getIPAddress(), 60, MINUTE) === false)
|
||||
{
|
||||
return Services::response()->setStatusCode(429);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* We don't have anything to do here.
|
||||
*
|
||||
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
|
||||
* @param ResponseInterface|\CodeIgniter\HTTP\Response $response
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function after(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
@ -12,8 +12,9 @@
|
||||
height: 200px;
|
||||
width: 155px;
|
||||
display: inline-block;
|
||||
opacity: 0.08;
|
||||
opacity: 0.12;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
top: 2rem;
|
||||
left: 50%;
|
||||
margin-left: -73px;
|
||||
@ -32,6 +33,8 @@
|
||||
margin-top: 145px;
|
||||
margin-bottom: 0;
|
||||
color: #222;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.wrap {
|
||||
max-width: 1024px;
|
||||
@ -81,15 +84,11 @@
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
<h1>Welcome to CodeIgniter</h1>
|
||||
|
||||
<p class="version">version <?= CodeIgniter\CodeIgniter::CI_VERSION ?></p>
|
||||
|
||||
<div class="logo">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="155.000000px" height="200.000000px" viewBox="0 0 155.000000 200.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none">
|
||||
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)" fill="#ee2600" stroke="none">
|
||||
<path d="M737 1963 c22 -79 -7 -185 -78 -290 -18 -26 -107 -122 -197 -213
|
||||
-239 -240 -336 -371 -403 -544 -79 -206 -78 -408 5 -582 64 -134 212 -264 361
|
||||
-314 l60 -20 -30 22 c-210 152 -229 387 -48 588 25 27 48 50 51 50 4 0 7 -27
|
||||
@ -104,6 +103,10 @@
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1>Welcome to CodeIgniter</h1>
|
||||
|
||||
<p class="version">version <?= CodeIgniter\CodeIgniter::CI_VERSION ?></p>
|
||||
|
||||
<div class="guide">
|
||||
<p>The page you are looking at is being generated dynamically by CodeIgniter.</p>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
"homepage": "https://codeigniter.com",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"php": ">=7.2",
|
||||
"ext-curl": "*",
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
@ -15,7 +15,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"codeigniter4/codeigniter4-standard": "^1.0",
|
||||
"mikey179/vfsStream": "1.6.*",
|
||||
"mikey179/vfsstream": "1.6.*",
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"squizlabs/php_codesniffer": "^3.3"
|
||||
},
|
||||
|
@ -35,7 +35,7 @@ If you change anything that requires a change to documentation then you will nee
|
||||
|
||||
### Compatibility
|
||||
|
||||
CodeIgniter4 requires PHP 7.1.
|
||||
CodeIgniter4 requires PHP 7.2.
|
||||
|
||||
### Branching
|
||||
|
||||
|
@ -54,7 +54,8 @@ If you've found a critical vulnerability, we'd be happy to credit you in our
|
||||
Tips for a Good Issue Report
|
||||
****************************
|
||||
|
||||
Use a descriptive subject line (eg parser library chokes on commas) rather than a vague one (eg. your code broke).
|
||||
Use a descriptive subject line (eg parser library chokes on commas) rather than
|
||||
a vague one (eg. your code broke).
|
||||
|
||||
Address a single issue in a report.
|
||||
|
||||
@ -64,12 +65,13 @@ Explain what you expected to happen, and what did happen.
|
||||
Include error messages and stacktrace, if any.
|
||||
|
||||
Include short code segments if they help to explain.
|
||||
Use a pastebin or dropbox facility to include longer segments of code or screenshots - do not include them in the issue report itself.
|
||||
Use a pastebin or dropbox facility to include longer segments of code or
|
||||
screenshots - do not include them in the issue report itself.
|
||||
This means setting a reasonable expiry for those, until the issue is resolved or closed.
|
||||
|
||||
If you know how to fix the issue, you can do so in your own fork & branch, and submit a pull request.
|
||||
The issue report information above should be part of that.
|
||||
|
||||
If your issue report can describe the steps to reproduce the problem, that is great.
|
||||
If you can include a unit test that reproduces the problem, that is even better, as it gives whoever is fixing
|
||||
it a clearer target!
|
||||
If you can include a unit test that reproduces the problem, that is even better,
|
||||
as it gives whoever is fixing it a clearer target!
|
||||
|
@ -64,16 +64,17 @@ Change Log
|
||||
|
||||
The change-log, in the user guide root, needs to be kept up-to-date.
|
||||
Not all changes will need an entry in it, but new classes, major or BC changes
|
||||
to existing classes, and bug fixes should.
|
||||
to existing classes should. Once we have a stable release, bug fixes would
|
||||
appear in the changelog too.
|
||||
|
||||
See the `CodeIgniter 4 change log
|
||||
<https://github.com/codeigniter4/CodeIgniter/blob/develop/user_guide_src/source/changelog.rst>`_
|
||||
for an example.
|
||||
The changelog is independently maintained by the framework release manager
|
||||
Make sure that your PR descriptions help us decide if the contribution should
|
||||
be highlighted in the next release after it has been merged.
|
||||
|
||||
PHP Compatibility
|
||||
=================
|
||||
|
||||
CodeIgniter4 requires PHP 7.1.
|
||||
CodeIgniter4 requires PHP 7.2.
|
||||
|
||||
Backwards Compatibility
|
||||
=======================
|
||||
@ -89,7 +90,7 @@ with earlier versions of the framework.
|
||||
Mergeability
|
||||
============
|
||||
|
||||
Your PRs need to be mergeable before they will be considered.
|
||||
Your PRs need to be mergeable and GPG-signed before they will be considered.
|
||||
|
||||
We suggest that you synchronize your repository's ``develop`` branch with
|
||||
that in the main repository, and then your feature branch and
|
||||
|
@ -2,18 +2,19 @@
|
||||
CodeIgniter Internals Overview
|
||||
##############################
|
||||
|
||||
This guide should help contributors understand how the core of the framework works, and what needs to be done
|
||||
when creating new functionality. Specifically, it details the information needed to create new packages for the
|
||||
core.
|
||||
This guide should help contributors understand how the core of the framework works,
|
||||
and what needs to be done when creating new functionality. Specifically, it
|
||||
details the information needed to create new packages for the core.
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
All packages should be designed to be completely isolated from the rest of the packages. This will allow
|
||||
them to be used in projects outside of CodeIgniter. Basically, this means that all dependencies should be
|
||||
kept to a minimum. Any dependencies must be able to be passed into the constructor. If you do need to use one
|
||||
of the other core packages, you can create that in the constructor using the Services class, as long as you
|
||||
provide a way for dependencies to override that::
|
||||
All packages should be designed to be completely isolated from the rest of the
|
||||
packages, if possible. This will allow them to be used in projects outside of CodeIgniter.
|
||||
Basically, this means that any dependencies should be kept to a minimum.
|
||||
Any dependencies must be able to be passed into the constructor. If you do need to use one
|
||||
of the other core packages, you can create that in the constructor using the
|
||||
Services class, as long as you provide a way for dependencies to override that::
|
||||
|
||||
public function __construct(Foo $foo=null)
|
||||
{
|
||||
@ -26,17 +27,18 @@ Type hinting
|
||||
============
|
||||
|
||||
PHP7 provides the ability to `type hint <http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration>`_
|
||||
method parameters and return types. Use it where possible. Return type hinting is not always practical, but do try to
|
||||
make it work.
|
||||
method parameters and return types. Use it where possible. Return type hinting
|
||||
is not always practical, but do try to make it work.
|
||||
|
||||
At this time, we are not using strict type hinting.
|
||||
|
||||
Abstractions
|
||||
============
|
||||
|
||||
The amount of abstraction required to implement a solution should be the minimal amount required. Every layer of
|
||||
abstraction brings additional levels of technical debt and unnecessary complexity. That said, don't be afraid to
|
||||
use it when it's needed and can help things.
|
||||
The amount of abstraction required to implement a solution should be the minimal
|
||||
amount required. Every layer of abstraction brings additional levels of technical
|
||||
debt and unnecessary complexity. That said, don't be afraid to use it when it's
|
||||
needed and can help things.
|
||||
|
||||
* Don't create a new container class when an array will do just fine.
|
||||
* Start simple, refactor as necessary to achieve clean separation of code, but don't overdo it.
|
||||
@ -44,73 +46,93 @@ use it when it's needed and can help things.
|
||||
Testing
|
||||
=======
|
||||
|
||||
Any new packages submitted to the framework must be accompanied by unit tests. The target is 80%+ coverage of all
|
||||
classes within the package.
|
||||
Any new packages submitted to the framework must be accompanied by unit tests.
|
||||
The target is 80%+ code coverage of all classes within the package.
|
||||
|
||||
* Test only public methods, not protected and private unless the method really needs it due to complexity.
|
||||
* Don't just test that the method works, but test for all fail states, thrown exceptions, and other pathways through your code.
|
||||
|
||||
You should be aware of the extra assertions that we have made, provisions for
|
||||
accessing private properties for testing, and mock services.
|
||||
We have also made a **CITestStreamFilter** to capture test output.
|
||||
Do check out similar tests in ``tests/system/``, and read the "Testing" section
|
||||
in the user guide, before you dig in to your own.
|
||||
|
||||
Some testing needs to be done in a separate process, in order to setup the
|
||||
PHP globals to mimic test situations properly. See
|
||||
``tests/system/HTTP/ResponseSendTest`` for an example of this.
|
||||
|
||||
Namespaces and Files
|
||||
====================
|
||||
|
||||
All new packages should live under the ``CodeIgniter`` namespace. The package itself will need its own sub-namespace
|
||||
All new packages should live under the ``CodeIgniter`` namespace.
|
||||
The package itself will need its own sub-namespace
|
||||
that collects all related files into one grouping, like ``CodeIgniter\HTTP``.
|
||||
|
||||
Files MUST be named the same as the class they hold, and they must match the :doc:`Style Guide <styleguide>`, meaning
|
||||
CamelCase class and file names. The should be in their own directory that matches the sub-namespace under the **system**
|
||||
directory.
|
||||
Files MUST be named the same as the class they hold, and they must match the
|
||||
:doc:`Style Guide <./styleguide.rst>`, meaning CamelCase class and file names.
|
||||
They should be in their own directory that matches the sub-namespace under the
|
||||
**system** directory.
|
||||
|
||||
The the Router as an example. The Router lives in the ``CodeIgniter\Router`` namespace. It has two classes,
|
||||
**RouteCollection** and **Router**, which are in the files, **system/Router/RouteCollection.php** and
|
||||
Take the Router class as an example. The Router lives in the ``CodeIgniter\Router``
|
||||
namespace. Its two main classes,
|
||||
**RouteCollection** and **Router**, are in the files **system/Router/RouteCollection.php** and
|
||||
**system/Router/Router.php** respectively.
|
||||
|
||||
Interfaces
|
||||
----------
|
||||
|
||||
Most base classes should have an interface defined for them. At the very least this allows them to be easily mocked
|
||||
and passed in other classes as a dependency without breaking the type-hinting. The interface names should match
|
||||
the name of the class with "Interface" appended to it, like ``RouteCollectionInterface``.
|
||||
Most base classes should have an interface defined for them.
|
||||
At the very least this allows them to be easily mocked
|
||||
and passed to other classes as a dependency, without breaking the type-hinting.
|
||||
The interface names should match the name of the class with "Interface" appended
|
||||
to it, like ``RouteCollectionInterface``.
|
||||
|
||||
The Router package mentioned above includes the
|
||||
``CodeIgniter\Router\RouterCollectionInterface`` and ``CodeIgniter\Router\RouterInterface``
|
||||
``CodeIgniter\Router\RouteCollectionInterface`` and ``CodeIgniter\Router\RouterInterface``
|
||||
interfaces to provide the abstractions for the two classes in the package.
|
||||
|
||||
Handlers
|
||||
--------
|
||||
|
||||
When a package supports multiple "drivers", the convention is to place them in a **Handlers** directory, and
|
||||
name the child classes as Handlers. You will often find that creating a ``BaseHandler`` the child classes can
|
||||
extend to be beneficial in keeping the code DRY.
|
||||
When a package supports multiple "drivers", the convention is to place them in
|
||||
a **Handlers** directory, and name the child classes as Handlers.
|
||||
You will often find that creating a ``BaseHandler``, that the child classes can
|
||||
extend, to be beneficial in keeping the code DRY.
|
||||
|
||||
See the Log and Session packages for examples.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Should the package require user-configurable settings, you should create a new file just for that package under
|
||||
**application/Config**. The file name should generally match the package name.
|
||||
Should the package require user-configurable settings, you should create a new
|
||||
file just for that package under **app/Config**.
|
||||
The file name should generally match the package name.
|
||||
|
||||
Autoloader
|
||||
==========
|
||||
|
||||
All files within the package should be added to **system/Config/AutoloadConfig.php**, in the "classmap" property.
|
||||
This is only used for core framework files, and helps to minimize file system scans and keep performance high.
|
||||
All files within the package should be added to **system/Config/AutoloadConfig.php**,
|
||||
in the "classmap" property. This is only used for core framework files, and helps
|
||||
to minimize file system scans and keep performance high.
|
||||
|
||||
Command-Line Support
|
||||
====================
|
||||
|
||||
CodeIgniter has never been known for it's strong CLI support. However, if your package could benefit from it, create a
|
||||
new file under **system/Commands**. The class contained within is simply a controller that is intended for CLI
|
||||
usage only. The ``index()`` method should provide a list of available commands provided by that package.
|
||||
CodeIgniter has never been known for it's strong CLI support. However, if your
|
||||
package could benefit from it, create a new file under **system/Commands**.
|
||||
The class contained within is simply a controller that is intended for CLI
|
||||
usage only. The ``index()`` method should provide a list of available commands
|
||||
provided by that package.
|
||||
|
||||
Routes must be added to **system/Config/Routes.php** using the ``cli()`` method to ensure it is not accessible
|
||||
through the browser, but is restricted to the CLI only.
|
||||
Routes must be added to **system/Config/Routes.php** using the ``cli()`` method
|
||||
to ensure it is not accessible through the browser, but is restricted to the CLI only.
|
||||
|
||||
See the **MigrationsCommand** file for an example.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
All packages must contain appropriate documentation that matches the tone and style of the rest of the user guide.
|
||||
In most cases, the top portion of the package's page should be treated in tutorial fashion, while the second
|
||||
half would be a class reference.
|
||||
All packages must contain appropriate documentation that matches the tone and
|
||||
style of the rest of the user guide. In most cases, the top portion of the package's
|
||||
page should be treated in tutorial fashion, while the second half would be a class reference.
|
||||
|
@ -9,24 +9,22 @@ The developer pushing a commit as part of a PR isn't necessarily the person
|
||||
who committed it originally, if the commit is not signed. This distorts the
|
||||
commit history and makes it hard to tell where code came from.
|
||||
|
||||
If a person "signs" a commit, they are free to use any name, specifically
|
||||
If a person "signs off" a commit, they are free to use any name, specifically
|
||||
one not their own. Again, the commit history cannot be relied on to determine
|
||||
the origin of the code, if one developer is spoofing another. A malicious person
|
||||
could commit bad code (for instance a virus) and make it look like another
|
||||
developer created it.
|
||||
|
||||
The best solution, while not fool-proof, is to "securely sign" your
|
||||
commits. Such commits are digitally signed, with a GPG-key, and
|
||||
commits. Such commits are digitally signed, with a GPG-key
|
||||
associated with your github account. It still isn't foolproof, because
|
||||
a malicious developer could create a bogus email and account, but it is
|
||||
more reliable than an unsigned or a "signed" commit.
|
||||
more reliable than an unsigned or a "signed-off by" commit.
|
||||
|
||||
If you don't sign your commits, we **may** accept your contribution,
|
||||
assuming it meets usefulness and contribution guidelines, but only
|
||||
if it isn't critical code and only after checking it carefully.
|
||||
If code performs an important role, we will insist that it be signed, and if
|
||||
it is critical code (however we interpret that), we will insist that your
|
||||
contributions be securely signed.
|
||||
If code performs an important role, we will insist that it be securely signed.
|
||||
|
||||
Read below to find out how to sign your commits :)
|
||||
|
||||
|
@ -25,14 +25,14 @@ which requires all pull requests to be sent to the "develop" branch. This is
|
||||
where the next planned version will be developed. The "master" branch will
|
||||
always contain the latest stable version and is kept clean so a "hotfix" (e.g:
|
||||
an emergency security patch) can be applied to master to create a new version,
|
||||
without worrying about other features holding it up. For this reason all
|
||||
without worrying about other features holding it up. For this reason, all
|
||||
commits need to be made to "develop" and any sent to "master" will be closed
|
||||
automatically. If you have multiple changes to submit, please place each
|
||||
change into their own branch on your fork.
|
||||
|
||||
One thing at a time: a pull request should only contain one change. That does
|
||||
not mean only one commit, but one change - however many commits it took. The
|
||||
reason for this is that if you change X and Y but send a pull request for both
|
||||
reason for this is that if you change X and Y but send a single pull request for both
|
||||
at the same time, we might really want X but disagree with Y, meaning we
|
||||
cannot merge the request. Using the Git-Flow branching model you can create
|
||||
new branches for both of these features and send two requests.
|
||||
@ -45,7 +45,8 @@ in your github account. You can make changes to your forked repository, while
|
||||
you cannot do the same with the shared one - you have to submit pull requests
|
||||
to it instead.
|
||||
|
||||
`Creating a fork <https://help.github.com/articles/fork-a-repo/>`_ is done through the Github website. Navigate to `our
|
||||
`Creating a fork <https://help.github.com/articles/fork-a-repo/>`_
|
||||
is done through the Github website. Navigate to `our
|
||||
repository <https://github.com/codeigniter4/CodeIgniter4>`_,
|
||||
click the **Fork** button in the top-right of the page, and choose which account or
|
||||
organization of yours should contain that fork.
|
||||
@ -70,7 +71,7 @@ Synching
|
||||
|
||||
Within your local repository, Git will have created an alias, **origin**, for the
|
||||
Github repository it is bound to. You want to create an alias for the shared
|
||||
repository, so that you can "synch" the two, making sure that your repository
|
||||
repository as well, so that you can "synch" the two, making sure that your repository
|
||||
includes any other contributions that have been merged by us into the shared repo::
|
||||
|
||||
git remote add upstream UPSTREAM_URL
|
||||
@ -98,8 +99,11 @@ Branching Revisited
|
||||
The top of this page talked about the **master** and **develop** branches.
|
||||
The *best practice* for your work is to create a *feature branch* locally,
|
||||
to hold a group of related changes (source, unit testing, documentation,
|
||||
change log, etc). This local branch should be named appropriately,
|
||||
for instance "fix/problem123" or "new/mind-reader".
|
||||
change log, etc).
|
||||
|
||||
This local branch should be named appropriately, for instance
|
||||
"fix/problem123" or "new/mind-reader". The slashes in these branch names is
|
||||
optional, and implies a sort of namespacing if used.
|
||||
|
||||
For instance, make sure you are in the *develop* branch, and create a
|
||||
new feature branch, based on *develop*, for a new feature you are creating::
|
||||
@ -113,7 +117,7 @@ Committing
|
||||
==========
|
||||
|
||||
Your local changes need to be *committed* to save them in your local repository.
|
||||
This is where `contribution signing <signing>`_ comes in.
|
||||
This is where `contribution signing <./signing.rst>`_ comes in.
|
||||
|
||||
You can have as many commits in a branch as you need to "get it right".
|
||||
For instance, to commit your work from a debugging session::
|
||||
@ -179,13 +183,13 @@ Make sure that the PR title is helpful for the maintainers and other developers.
|
||||
Add any comments appropriate, for instance asking for review.
|
||||
|
||||
.. note::
|
||||
If you do not provide a title for your PR, the odds of it being summarily rejected
|
||||
If you do not provide a title or description for your PR, the odds of it being summarily rejected
|
||||
rise astronomically.
|
||||
|
||||
When your PR is submitted, a continuous integration task will be triggered,
|
||||
running all the unit tests as well as any other checking we have configured for it.
|
||||
If the unit tests fail, or if there are merge conflicts, your PR will not
|
||||
be mergeable until fixed.
|
||||
be mergeable until those are fixed.
|
||||
|
||||
Fix such changes locally, commit them properly, and then push your branch again.
|
||||
That will update the PR automatically, and re-run the CI tests. You don't need
|
||||
|
6
env
6
env
@ -10,6 +10,12 @@
|
||||
# at the beginning of the line.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# ENVIRONMENT
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# CI_ENVIRONMENT = production
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# APP
|
||||
#--------------------------------------------------------------------
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
// Valid PHP Version?
|
||||
$minPHPVersion = '7.1';
|
||||
$minPHPVersion = '7.2';
|
||||
if (phpversion() < $minPHPVersion)
|
||||
{
|
||||
die("Your PHP version must be {$minPHPVersion} or higher to run CodeIgniter. Current version: " . phpversion());
|
||||
@ -14,6 +14,7 @@ define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR);
|
||||
// Location of the Paths config file.
|
||||
// This is the line that might need to be changed, depending on your folder structure.
|
||||
$pathsPath = FCPATH . '../app/Config/Paths.php';
|
||||
// ^^^ Change this if you move your application folder
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------
|
||||
|
1
spark
1
spark
@ -34,6 +34,7 @@ define('FCPATH', __DIR__ . '/public' . DIRECTORY_SEPARATOR);
|
||||
|
||||
// Load our paths config file
|
||||
require 'app/Config/Paths.php';
|
||||
// ^^^ Change this line if you move your application folder
|
||||
|
||||
$paths = new Config\Paths();
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php namespace CodeIgniter\Autoloader;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
*
|
||||
@ -97,10 +99,11 @@ class Autoloader
|
||||
* the valid parts that we'll need.
|
||||
*
|
||||
* @param \Config\Autoload $config
|
||||
* @param \Config\Modules $moduleConfig
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function initialize(\Config\Autoload $config)
|
||||
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.
|
||||
@ -119,7 +122,11 @@ class Autoloader
|
||||
$this->classmap = $config->classmap;
|
||||
}
|
||||
|
||||
unset($config);
|
||||
// Should we load through Composer's namespaces, also?
|
||||
if ($moduleConfig->discoverInComposer)
|
||||
{
|
||||
$this->discoverComposerNamespaces();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -303,8 +310,8 @@ class Autoloader
|
||||
|
||||
/**
|
||||
* Attempts to load the class from common locations in previous
|
||||
* version of CodeIgniter, namely 'application/libraries', and
|
||||
* 'application/Models'.
|
||||
* version of CodeIgniter, namely 'app/Libraries', and
|
||||
* 'app/Models'.
|
||||
*
|
||||
* @param string $class The class name. This typically should NOT have a namespace.
|
||||
*
|
||||
@ -391,5 +398,37 @@ class Autoloader
|
||||
|
||||
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 paths with trailng slash. We don't.
|
||||
$newPaths = [];
|
||||
foreach ($paths as $key => $value)
|
||||
{
|
||||
$newPaths[rtrim($key, '\\ ')] = $value;
|
||||
}
|
||||
|
||||
$this->prefixes = array_merge($this->prefixes, $newPaths);
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +119,8 @@ class FileLocator
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$path = $this->getNamespaces($prefix);
|
||||
$path = $this->getNamespaces($prefix);
|
||||
|
||||
$filename = implode('/', $segments);
|
||||
break;
|
||||
}
|
||||
@ -201,8 +202,8 @@ class FileLocator
|
||||
* $locator->search('Config/Routes.php');
|
||||
* // Assuming PSR4 namespaces include foo and bar, might return:
|
||||
* [
|
||||
* 'application/modules/foo/Config/Routes.php',
|
||||
* 'application/modules/bar/Config/Routes.php',
|
||||
* 'app/Modules/foo/Config/Routes.php',
|
||||
* 'app/Modules/bar/Config/Routes.php',
|
||||
* ]
|
||||
*
|
||||
* @param string $path
|
||||
@ -268,7 +269,9 @@ class FileLocator
|
||||
{
|
||||
$path = $this->autoloader->getNamespace($prefix);
|
||||
|
||||
return isset($path[0]) ? $path[0] : '';
|
||||
return isset($path[0])
|
||||
? rtrim($path[0], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR
|
||||
: '';
|
||||
}
|
||||
|
||||
$namespaces = [];
|
||||
@ -279,7 +282,7 @@ class FileLocator
|
||||
{
|
||||
$namespaces[] = [
|
||||
'prefix' => $prefix,
|
||||
'path' => $path,
|
||||
'path' => rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ class RedisHandler implements CacheInterface
|
||||
'password' => null,
|
||||
'port' => 6379,
|
||||
'timeout' => 0,
|
||||
'database' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -116,7 +117,12 @@ class RedisHandler implements CacheInterface
|
||||
|
||||
if (isset($config['password']) && ! $this->redis->auth($config['password']))
|
||||
{
|
||||
// log_message('error', 'Cache: Redis authentication failed.');
|
||||
log_message('error', 'Cache: Redis authentication failed.');
|
||||
}
|
||||
|
||||
if (isset($config['database']) && ! $this->redis->select($config['database']))
|
||||
{
|
||||
log_message('error', 'Cache: Redis select database failed.');
|
||||
}
|
||||
}
|
||||
catch (\RedisException $e)
|
||||
|
@ -61,7 +61,7 @@ class CodeIgniter
|
||||
/**
|
||||
* The current version of CodeIgniter Framework
|
||||
*/
|
||||
const CI_VERSION = '4.0.0-alpha.4';
|
||||
const CI_VERSION = '4.0.0-beta.1';
|
||||
|
||||
/**
|
||||
* App startup time.
|
||||
@ -199,7 +199,7 @@ class CodeIgniter
|
||||
* @param \CodeIgniter\Router\RouteCollectionInterface $routes
|
||||
* @param boolean $returnResponse
|
||||
*
|
||||
* @throws \CodeIgniter\HTTP\RedirectException
|
||||
* @throws \CodeIgniter\Filters\Exceptions\FilterException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function run(RouteCollectionInterface $routes = null, bool $returnResponse = false)
|
||||
@ -234,7 +234,7 @@ class CodeIgniter
|
||||
{
|
||||
return $this->handleRequest($routes, $cacheConfig, $returnResponse);
|
||||
}
|
||||
catch (Router\RedirectException $e)
|
||||
catch (\CodeIgniter\Filters\Exceptions\FilterException $e)
|
||||
{
|
||||
$logger = Services::logger();
|
||||
$logger->info('REDIRECTED ROUTE at ' . $e->getMessage());
|
||||
@ -533,7 +533,7 @@ class CodeIgniter
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool|\CodeIgniter\HTTP\ResponseInterface
|
||||
*/
|
||||
public function displayCache($config)
|
||||
{
|
||||
@ -564,7 +564,9 @@ class CodeIgniter
|
||||
$this->response->setBody($output);
|
||||
|
||||
return $this->response;
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -574,7 +576,7 @@ class CodeIgniter
|
||||
*
|
||||
* @param integer $time
|
||||
*
|
||||
* @return $this
|
||||
* @return void
|
||||
*/
|
||||
public static function cache(int $time)
|
||||
{
|
||||
|
@ -117,7 +117,7 @@ class MigrateRollback extends BaseCommand
|
||||
}
|
||||
try
|
||||
{
|
||||
if (! $this->isAllNamespace())
|
||||
if (! $this->isAllNamespace($params))
|
||||
{
|
||||
$namespace = $params['-n'] ?? CLI::getOption('n');
|
||||
$runner->version(0, $namespace);
|
||||
|
@ -48,7 +48,7 @@ use CodeIgniter\CLI\CLI;
|
||||
*/
|
||||
class Serve extends BaseCommand
|
||||
{
|
||||
protected $minPHPVersion = '7.1';
|
||||
protected $minPHPVersion = '7.2';
|
||||
|
||||
protected $group = 'CodeIgniter';
|
||||
protected $name = 'serve';
|
||||
|
@ -102,6 +102,35 @@ if (! function_exists('config'))
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
if (! function_exists('db_connnect'))
|
||||
{
|
||||
/**
|
||||
* Grabs a database connection and returns it to the user.
|
||||
*
|
||||
* This is a convenience wrapper for \Config\Database::connect()
|
||||
* and supports the same parameters. Namely:
|
||||
*
|
||||
* When passing in $db, you may pass any of the following to connect:
|
||||
* - group name
|
||||
* - existing connection instance
|
||||
* - array of database configuration values
|
||||
*
|
||||
* If $getShared === false then a new connection instance will be provided,
|
||||
* otherwise it will all calls will return the same instance.
|
||||
*
|
||||
* @param \CodeIgniter\Database\ConnectionInterface|array|string $db
|
||||
* @param boolean $getShared
|
||||
*
|
||||
* @return \CodeIgniter\Database\BaseConnection
|
||||
*/
|
||||
function db_connect($db = null, bool $getShared = true)
|
||||
{
|
||||
return \Config\Database::connect($db, $getShared);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
if (! function_exists('view'))
|
||||
{
|
||||
/**
|
||||
@ -540,7 +569,7 @@ if (! function_exists('helper'))
|
||||
* both in and out of the 'helpers' directory of a namespaced directory.
|
||||
*
|
||||
* Will load ALL helpers of the matching name, in the following order:
|
||||
* 1. application/Helpers
|
||||
* 1. app/Helpers
|
||||
* 2. {namespace}/Helpers
|
||||
* 3. system/Helpers
|
||||
*
|
||||
@ -571,44 +600,62 @@ if (! function_exists('helper'))
|
||||
$filename .= '_helper';
|
||||
}
|
||||
|
||||
$paths = $loader->search('Helpers/' . $filename);
|
||||
|
||||
if (! empty($paths))
|
||||
// If the file is namespaced, we'll just grab that
|
||||
// file and not search for any others
|
||||
if (strpos($filename, '\\') !== false)
|
||||
{
|
||||
foreach ($paths as $path)
|
||||
$path = $loader->locateFile($filename, 'Helpers');
|
||||
|
||||
if (empty($path))
|
||||
{
|
||||
if (strpos($path, APPPATH) === 0)
|
||||
throw \CodeIgniter\Files\Exceptions\FileNotFoundException::forFileNotFound($filename);
|
||||
}
|
||||
|
||||
$includes[] = $path;
|
||||
}
|
||||
|
||||
// No namespaces, so search in all available locations
|
||||
else
|
||||
{
|
||||
$paths = $loader->search('Helpers/' . $filename);
|
||||
|
||||
if (! empty($paths))
|
||||
{
|
||||
foreach ($paths as $path)
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
$appHelper = $path;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
elseif (strpos($path, SYSTEMPATH) === 0)
|
||||
{
|
||||
$systemHelper = $path;
|
||||
}
|
||||
else
|
||||
{
|
||||
$localIncludes[] = $path;
|
||||
if (strpos($path, APPPATH) === 0)
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
$appHelper = $path;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
elseif (strpos($path, SYSTEMPATH) === 0)
|
||||
{
|
||||
$systemHelper = $path;
|
||||
}
|
||||
else
|
||||
{
|
||||
$localIncludes[] = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// App-level helpers should override all others
|
||||
if (! empty($appHelper))
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
$includes[] = $appHelper;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
// App-level helpers should override all others
|
||||
if (! empty($appHelper))
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
$includes[] = $appHelper;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
// All namespaced files get added in next
|
||||
$includes = array_merge($includes, $localIncludes);
|
||||
// All namespaced files get added in next
|
||||
$includes = array_merge($includes, $localIncludes);
|
||||
|
||||
// And the system default one should be added in last.
|
||||
if (! empty($systemHelper))
|
||||
{
|
||||
$includes[] = $systemHelper;
|
||||
// And the system default one should be added in last.
|
||||
if (! empty($systemHelper))
|
||||
{
|
||||
$includes[] = $systemHelper;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ class BaseService
|
||||
|
||||
if ($init_autoloader)
|
||||
{
|
||||
static::autoloader()->initialize(new \Config\Autoload());
|
||||
static::autoloader()->initialize(new \Config\Autoload(), new \Config\Modules());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,6 +97,16 @@ class Config
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Resets the instances array
|
||||
*/
|
||||
public static function reset()
|
||||
{
|
||||
static::$instances = [];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Find configuration class and create instance
|
||||
*
|
||||
|
@ -466,7 +466,7 @@ class Services extends BaseService
|
||||
*
|
||||
* @return \CodeIgniter\View\Parser
|
||||
*/
|
||||
public static function parser($viewPath = APPPATH . 'Views/', $config = null, bool $getShared = true)
|
||||
public static function parser($viewPath = null, $config = null, bool $getShared = true)
|
||||
{
|
||||
if ($getShared)
|
||||
{
|
||||
@ -478,6 +478,12 @@ class Services extends BaseService
|
||||
$config = new \Config\View();
|
||||
}
|
||||
|
||||
if (is_null($viewPath))
|
||||
{
|
||||
$paths = config('Paths');
|
||||
$viewPath = $paths->viewDirectory;
|
||||
}
|
||||
|
||||
return new \CodeIgniter\View\Parser($config, $viewPath, static::locator(true), CI_DEBUG, static::logger(true));
|
||||
}
|
||||
|
||||
@ -808,7 +814,7 @@ class Services extends BaseService
|
||||
|
||||
if (is_null($config))
|
||||
{
|
||||
$config = new \Config\Validation();
|
||||
$config = config('Validation');
|
||||
}
|
||||
|
||||
return new \CodeIgniter\Validation\Validation($config, static::renderer());
|
||||
|
@ -35,7 +35,7 @@
|
||||
* @since Version 3.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
use CodeIgniter\Config\Services;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use CodeIgniter\Validation\Validation;
|
||||
@ -106,7 +106,7 @@ class Controller
|
||||
* @param ResponseInterface $response
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @throws \CodeIgniter\HTTP\RedirectException
|
||||
* @throws \CodeIgniter\HTTP\Exceptions\HTTPException
|
||||
*/
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
{
|
||||
@ -135,7 +135,7 @@ class Controller
|
||||
* considered secure for. Only with HSTS header.
|
||||
* Default value is 1 year.
|
||||
*
|
||||
* @throws \CodeIgniter\HTTP\RedirectException
|
||||
* @throws \CodeIgniter\HTTP\Exceptions\HTTPException
|
||||
*/
|
||||
public function forceHTTPS(int $duration = 31536000)
|
||||
{
|
||||
@ -179,8 +179,8 @@ class Controller
|
||||
* A shortcut to performing validation on input data. If validation
|
||||
* is not successful, a $errors property will be set on this class.
|
||||
*
|
||||
* @param array $rules
|
||||
* @param array $messages An array of custom error messages
|
||||
* @param array|string $rules
|
||||
* @param array $messages An array of custom error messages
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
|
@ -664,7 +664,7 @@ class BaseBuilder
|
||||
$op = $this->getOperator($k);
|
||||
$k = trim(str_replace($op, '', $k));
|
||||
|
||||
$bind = $this->setBind($k, $v);
|
||||
$bind = $this->setBind($k, $v, $escape);
|
||||
|
||||
if (empty($op))
|
||||
{
|
||||
@ -813,8 +813,8 @@ class BaseBuilder
|
||||
|
||||
$not = ($not) ? ' NOT' : '';
|
||||
|
||||
$where_in = array_values($values);
|
||||
$this->binds[$ok] = $where_in;
|
||||
$where_in = array_values($values);
|
||||
$ok = $this->setBind($ok, $where_in, $escape);
|
||||
|
||||
$prefix = empty($this->QBWhere) ? $this->groupGetType('') : $this->groupGetType($type);
|
||||
|
||||
@ -955,19 +955,19 @@ class BaseBuilder
|
||||
|
||||
if ($side === 'none')
|
||||
{
|
||||
$bind = $this->setBind($k, $v);
|
||||
$bind = $this->setBind($k, $v, $escape);
|
||||
}
|
||||
elseif ($side === 'before')
|
||||
{
|
||||
$bind = $this->setBind($k, "%$v");
|
||||
$bind = $this->setBind($k, "%$v", $escape);
|
||||
}
|
||||
elseif ($side === 'after')
|
||||
{
|
||||
$bind = $this->setBind($k, "$v%");
|
||||
$bind = $this->setBind($k, "$v%", $escape);
|
||||
}
|
||||
else
|
||||
{
|
||||
$bind = $this->setBind($k, "%$v%");
|
||||
$bind = $this->setBind($k, "%$v%", $escape);
|
||||
}
|
||||
|
||||
$like_statement = $this->_like_statement($prefix, $k, $not, $bind, $insensitiveSearch);
|
||||
@ -1345,7 +1345,7 @@ class BaseBuilder
|
||||
{
|
||||
if ($escape)
|
||||
{
|
||||
$bind = $this->setBind($k, $v);
|
||||
$bind = $this->setBind($k, $v, $escape);
|
||||
$this->QBSet[$this->db->protectIdentifiers($k, false, $escape)] = ":$bind:";
|
||||
}
|
||||
else
|
||||
@ -1399,11 +1399,32 @@ class BaseBuilder
|
||||
$this->resetSelect();
|
||||
}
|
||||
|
||||
return $select;
|
||||
return $this->compileFinalQuery($select);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a finalized, compiled query string with the bindings
|
||||
* inserted and prefixes swapped out.
|
||||
*
|
||||
* @param string $sql
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
protected function compileFinalQuery(string $sql): string
|
||||
{
|
||||
$query = new Query($this->db);
|
||||
$query->setQuery($sql, $this->binds, false);
|
||||
|
||||
if (! empty($this->db->swapPre) && ! empty($this->db->DBPrefix))
|
||||
{
|
||||
$query->swapPrefix($this->db->DBPrefix, $this->db->swapPre);
|
||||
}
|
||||
|
||||
return $query->getQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get
|
||||
*
|
||||
@ -1423,7 +1444,10 @@ class BaseBuilder
|
||||
{
|
||||
$this->limit($limit, $offset);
|
||||
}
|
||||
$result = $returnSQL ? $this->getCompiledSelect() : $this->db->query($this->compileSelect(), $this->binds);
|
||||
|
||||
$result = $returnSQL
|
||||
? $this->getCompiledSelect()
|
||||
: $this->db->query($this->compileSelect(), $this->binds, false);
|
||||
|
||||
if ($reset === true)
|
||||
{
|
||||
@ -1461,7 +1485,7 @@ class BaseBuilder
|
||||
return $sql;
|
||||
}
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
$query = $this->db->query($sql, null, false);
|
||||
if (empty($query->getResult()))
|
||||
{
|
||||
return 0;
|
||||
@ -1510,7 +1534,7 @@ class BaseBuilder
|
||||
return $sql;
|
||||
}
|
||||
|
||||
$result = $this->db->query($sql, $this->binds);
|
||||
$result = $this->db->query($sql, $this->binds, false);
|
||||
|
||||
if ($reset === true)
|
||||
{
|
||||
@ -1559,7 +1583,7 @@ class BaseBuilder
|
||||
$this->limit($limit, $offset);
|
||||
}
|
||||
|
||||
$result = $this->db->query($this->compileSelect(), $this->binds);
|
||||
$result = $this->db->query($this->compileSelect(), $this->binds, false);
|
||||
$this->resetSelect();
|
||||
|
||||
return $result;
|
||||
@ -1624,7 +1648,7 @@ class BaseBuilder
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->db->query($sql, $this->binds);
|
||||
$this->db->query($sql, $this->binds, false);
|
||||
$affected_rows += $this->db->affectedRows();
|
||||
}
|
||||
}
|
||||
@ -1696,7 +1720,7 @@ class BaseBuilder
|
||||
$clean = [];
|
||||
foreach ($row as $k => $value)
|
||||
{
|
||||
$clean[] = ':' . $this->setBind($k, $value) . ':';
|
||||
$clean[] = ':' . $this->setBind($k, $value, $escape) . ':';
|
||||
}
|
||||
|
||||
$row = $clean;
|
||||
@ -1741,7 +1765,7 @@ class BaseBuilder
|
||||
$this->resetWrite();
|
||||
}
|
||||
|
||||
return $sql;
|
||||
return $this->compileFinalQuery($sql);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -1779,7 +1803,7 @@ class BaseBuilder
|
||||
{
|
||||
$this->resetWrite();
|
||||
|
||||
$result = $this->db->query($sql, $this->binds);
|
||||
$result = $this->db->query($sql, $this->binds, false);
|
||||
|
||||
// Clear our binds so we don't eat up memory
|
||||
$this->binds = [];
|
||||
@ -1868,7 +1892,7 @@ class BaseBuilder
|
||||
|
||||
$this->resetWrite();
|
||||
|
||||
return $returnSQL ? $sql : $this->db->query($sql, $this->binds);
|
||||
return $returnSQL ? $sql : $this->db->query($sql, $this->binds, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -1931,7 +1955,7 @@ class BaseBuilder
|
||||
$this->resetWrite();
|
||||
}
|
||||
|
||||
return $sql;
|
||||
return $this->compileFinalQuery($sql);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -1981,7 +2005,7 @@ class BaseBuilder
|
||||
{
|
||||
$this->resetWrite();
|
||||
|
||||
if ($this->db->query($sql, $this->binds))
|
||||
if ($this->db->query($sql, $this->binds, false))
|
||||
{
|
||||
// Clear our binds so we don't eat up memory
|
||||
$this->binds = [];
|
||||
@ -2115,7 +2139,7 @@ class BaseBuilder
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->db->query($sql, $this->binds);
|
||||
$this->db->query($sql, $this->binds, false);
|
||||
$affected_rows += $this->db->affectedRows();
|
||||
}
|
||||
|
||||
@ -2203,7 +2227,7 @@ class BaseBuilder
|
||||
$index_set = true;
|
||||
}
|
||||
|
||||
$bind = $this->setBind($k2, $v2);
|
||||
$bind = $this->setBind($k2, $v2, $escape);
|
||||
|
||||
$clean[$this->db->protectIdentifiers($k2, false, $escape)] = ":$bind:";
|
||||
}
|
||||
@ -2242,7 +2266,7 @@ class BaseBuilder
|
||||
|
||||
$this->resetWrite();
|
||||
|
||||
return $this->db->query($sql);
|
||||
return $this->db->query($sql, null, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -2271,7 +2295,7 @@ class BaseBuilder
|
||||
|
||||
$this->resetWrite();
|
||||
|
||||
return $this->db->query($sql);
|
||||
return $this->db->query($sql, null, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -2312,7 +2336,7 @@ class BaseBuilder
|
||||
$sql = $this->delete($table, '', null, $reset);
|
||||
$this->returnDeleteSQL = false;
|
||||
|
||||
return $sql;
|
||||
return $this->compileFinalQuery($sql);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -2371,7 +2395,7 @@ class BaseBuilder
|
||||
$this->resetWrite();
|
||||
}
|
||||
|
||||
return ($returnSQL === true) ? $sql : $this->db->query($sql, $this->binds);
|
||||
return ($returnSQL === true) ? $sql : $this->db->query($sql, $this->binds, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -2390,7 +2414,7 @@ class BaseBuilder
|
||||
|
||||
$sql = $this->_update($this->QBFrom[0], [$column => "{$column} + {$value}"]);
|
||||
|
||||
return $this->db->query($sql, $this->binds);
|
||||
return $this->db->query($sql, $this->binds, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -2409,7 +2433,7 @@ class BaseBuilder
|
||||
|
||||
$sql = $this->_update($this->QBFrom[0], [$column => "{$column}-{$value}"]);
|
||||
|
||||
return $this->db->query($sql, $this->binds);
|
||||
return $this->db->query($sql, $this->binds, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -2918,17 +2942,24 @@ class BaseBuilder
|
||||
|
||||
/**
|
||||
* Stores a bind value after ensuring that it's unique.
|
||||
* While it might be nicer to have named keys for our binds array
|
||||
* with PHP 7+ we get a huge memory/performance gain with indexed
|
||||
* arrays instead, so lets take advantage of that here.
|
||||
*
|
||||
* @param string $key
|
||||
* @param null $value
|
||||
* @param string $key
|
||||
* @param null $value
|
||||
* @param boolean $escape
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function setBind(string $key, $value = null)
|
||||
protected function setBind(string $key, $value = null, bool $escape = true)
|
||||
{
|
||||
if (! array_key_exists($key, $this->binds))
|
||||
{
|
||||
$this->binds[$key] = $value;
|
||||
$this->binds[$key] = [
|
||||
$value,
|
||||
$escape,
|
||||
];
|
||||
|
||||
return $key;
|
||||
}
|
||||
@ -2937,10 +2968,13 @@ class BaseBuilder
|
||||
|
||||
while (array_key_exists($key . $count, $this->binds))
|
||||
{
|
||||
++ $count;
|
||||
++$count;
|
||||
}
|
||||
|
||||
$this->binds[$key . $count] = $value;
|
||||
$this->binds[$key . $count] = [
|
||||
$value,
|
||||
$escape,
|
||||
];
|
||||
|
||||
return $key . $count;
|
||||
}
|
||||
|
@ -596,12 +596,14 @@ abstract class BaseConnection implements ConnectionInterface
|
||||
* Should automatically handle different connections for read/write
|
||||
* queries if needed.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array ...$binds
|
||||
* @param string $queryClass
|
||||
* @param string $sql
|
||||
* @param array ...$binds
|
||||
* @param boolean $setEscapeFlags
|
||||
* @param string $queryClass
|
||||
*
|
||||
* @return BaseResult|Query|false
|
||||
*/
|
||||
public function query(string $sql, $binds = null, $queryClass = 'CodeIgniter\\Database\\Query')
|
||||
public function query(string $sql, $binds = null, bool $setEscapeFlags = true, $queryClass = 'CodeIgniter\\Database\\Query')
|
||||
{
|
||||
if (empty($this->connID))
|
||||
{
|
||||
@ -609,13 +611,12 @@ abstract class BaseConnection implements ConnectionInterface
|
||||
}
|
||||
|
||||
$resultClass = str_replace('Connection', 'Result', get_class($this));
|
||||
|
||||
/**
|
||||
* @var Query $query
|
||||
*/
|
||||
$query = new $queryClass($this);
|
||||
|
||||
$query->setQuery($sql, $binds);
|
||||
$query->setQuery($sql, $binds, $setEscapeFlags);
|
||||
|
||||
if (! empty($this->swapPre) && ! empty($this->DBPrefix))
|
||||
{
|
||||
@ -1709,6 +1710,20 @@ abstract class BaseConnection implements ConnectionInterface
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Empties our data cache. Especially helpful during testing.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function resetDataCache()
|
||||
{
|
||||
$this->dataCache = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the last error code and message.
|
||||
*
|
||||
|
@ -74,6 +74,12 @@ class Config extends BaseConfig
|
||||
*/
|
||||
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;
|
||||
@ -135,44 +141,7 @@ class Config extends BaseConfig
|
||||
*/
|
||||
public static function forge($group = null)
|
||||
{
|
||||
// Allow custom connections to be sent in
|
||||
if (is_array($group))
|
||||
{
|
||||
$config = $group;
|
||||
$group = 'custom-' . md5(json_encode($config));
|
||||
}
|
||||
else
|
||||
{
|
||||
$config = config('Database');
|
||||
}
|
||||
|
||||
static::ensureFactory();
|
||||
|
||||
if (empty($group))
|
||||
{
|
||||
$group = ENVIRONMENT === 'testing' ? 'tests' : $config->defaultGroup;
|
||||
}
|
||||
|
||||
if (is_string($group) && ! isset($config->$group) && ! is_array($config))
|
||||
{
|
||||
throw new \InvalidArgumentException($group . ' is not a valid database connection group.');
|
||||
}
|
||||
|
||||
if (! isset(static::$instances[$group]))
|
||||
{
|
||||
if (is_array($config))
|
||||
{
|
||||
$db = static::connect($config);
|
||||
}
|
||||
else
|
||||
{
|
||||
$db = static::connect($group);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$db = static::$instances[$group];
|
||||
}
|
||||
$db = static::connect($group);
|
||||
|
||||
return static::$factory->loadForge($db);
|
||||
}
|
||||
@ -182,50 +151,13 @@ class Config extends BaseConfig
|
||||
/**
|
||||
* Returns a new instance of the Database Utilities class.
|
||||
*
|
||||
* @param string|null $group
|
||||
* @param string|array|null $group
|
||||
*
|
||||
* @return BaseUtils
|
||||
*/
|
||||
public static function utils(string $group = null)
|
||||
public static function utils($group = null)
|
||||
{
|
||||
// Allow custom connections to be sent in
|
||||
if (is_array($group))
|
||||
{
|
||||
$config = $group;
|
||||
$group = 'custom-' . md5(json_encode($config));
|
||||
}
|
||||
else
|
||||
{
|
||||
$config = config('Database');
|
||||
}
|
||||
|
||||
static::ensureFactory();
|
||||
|
||||
if (empty($group))
|
||||
{
|
||||
$group = ENVIRONMENT === 'testing' ? 'tests' : $config->defaultGroup;
|
||||
}
|
||||
|
||||
if (is_string($group) && ! isset($config->$group) && ! is_array($config))
|
||||
{
|
||||
throw new \InvalidArgumentException($group . ' is not a valid database connection group.');
|
||||
}
|
||||
|
||||
if (! isset(static::$instances[$group]))
|
||||
{
|
||||
if (is_array($config))
|
||||
{
|
||||
$db = static::connect($config);
|
||||
}
|
||||
else
|
||||
{
|
||||
$db = static::connect($group);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$db = static::$instances[$group];
|
||||
}
|
||||
$db = static::connect($group);
|
||||
|
||||
return static::$factory->loadUtils($db);
|
||||
}
|
||||
@ -241,7 +173,7 @@ class Config extends BaseConfig
|
||||
*/
|
||||
public static function seeder(string $group = null)
|
||||
{
|
||||
$config = new \Config\Database();
|
||||
$config = config('Database');
|
||||
|
||||
return new Seeder($config, static::connect($group));
|
||||
}
|
||||
|
@ -45,4 +45,9 @@ class DataException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
return new static(lang('Database.invalidAllowedFields', [$model]));
|
||||
}
|
||||
|
||||
public static function forTableNotFound(string $table)
|
||||
{
|
||||
return new static(lang('Database.tableNotFound', [$table]));
|
||||
}
|
||||
}
|
||||
|
@ -468,7 +468,7 @@ class Forge
|
||||
|
||||
if (is_bool($sql))
|
||||
{
|
||||
$this->_reset();
|
||||
$this->reset();
|
||||
if ($sql === false)
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
@ -494,7 +494,7 @@ class Forge
|
||||
}
|
||||
}
|
||||
|
||||
$this->_reset();
|
||||
$this->reset();
|
||||
|
||||
return $result;
|
||||
}
|
||||
@ -730,7 +730,7 @@ class Forge
|
||||
}
|
||||
|
||||
$sqls = $this->_alterTable('ADD', $this->db->DBPrefix . $table, $this->_processFields());
|
||||
$this->_reset();
|
||||
$this->reset();
|
||||
if ($sqls === false)
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
@ -806,7 +806,7 @@ class Forge
|
||||
}
|
||||
|
||||
$sqls = $this->_alterTable('CHANGE', $this->db->DBPrefix . $table, $this->_processFields());
|
||||
$this->_reset();
|
||||
$this->reset();
|
||||
if ($sqls === false)
|
||||
{
|
||||
if ($this->db->DBDebug)
|
||||
@ -817,11 +817,14 @@ class Forge
|
||||
return false;
|
||||
}
|
||||
|
||||
for ($i = 0, $c = count($sqls); $i < $c; $i++)
|
||||
if ($sqls !== null)
|
||||
{
|
||||
if ($this->db->query($sqls[$i]) === false)
|
||||
for ($i = 0, $c = count($sqls); $i < $c; $i++)
|
||||
{
|
||||
return false;
|
||||
if ($this->db->query($sqls[$i]) === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1251,7 +1254,7 @@ class Forge
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _reset()
|
||||
public function reset()
|
||||
{
|
||||
$this->fields = $this->keys = $this->uniqueKeys = $this->primaryKeys = $this->foreignKeys = [];
|
||||
}
|
||||
|
@ -126,17 +126,37 @@ class MigrationRunner
|
||||
*/
|
||||
protected $cliMessages = [];
|
||||
|
||||
/**
|
||||
* Tracks whether we have already ensured
|
||||
* the table exists or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $tableChecked = false;
|
||||
|
||||
/**
|
||||
* The full path to locate migration files.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param BaseConfig $config
|
||||
* @param \CodeIgniter\Database\ConnectionInterface $db
|
||||
* When passing in $db, you may pass any of the following to connect:
|
||||
* - group name
|
||||
* - existing connection instance
|
||||
* - array of database configuration values
|
||||
*
|
||||
* @param BaseConfig $config
|
||||
* @param \CodeIgniter\Database\ConnectionInterface|array|string $db
|
||||
*
|
||||
* @throws ConfigException
|
||||
*/
|
||||
public function __construct(BaseConfig $config, ConnectionInterface $db = null)
|
||||
public function __construct(BaseConfig $config, $db = null)
|
||||
{
|
||||
$this->enabled = $config->enabled ?? false;
|
||||
$this->type = $config->type ?? 'timestamp';
|
||||
@ -147,15 +167,10 @@ class MigrationRunner
|
||||
$this->namespace = APP_NAMESPACE;
|
||||
|
||||
// get default database group
|
||||
$config = new \Config\Database();
|
||||
$config = config('Database');
|
||||
$this->group = $config->defaultGroup;
|
||||
unset($config);
|
||||
|
||||
if (empty($this->table))
|
||||
{
|
||||
throw ConfigException::forMissingMigrationsTable();
|
||||
}
|
||||
|
||||
if (! in_array($this->type, ['sequential', 'timestamp']))
|
||||
{
|
||||
throw ConfigException::forInvalidMigrationType($this->type);
|
||||
@ -166,9 +181,7 @@ class MigrationRunner
|
||||
|
||||
// If no db connection passed in, use
|
||||
// default database group.
|
||||
$this->db = ! empty($db) ? $db : \Config\Database::connect();
|
||||
|
||||
$this->ensureTable();
|
||||
$this->db = db_connect($db);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -192,6 +205,9 @@ class MigrationRunner
|
||||
{
|
||||
throw ConfigException::forDisabledMigrations();
|
||||
}
|
||||
|
||||
$this->ensureTable();
|
||||
|
||||
// Set Namespace if not null
|
||||
if (! is_null($namespace))
|
||||
{
|
||||
@ -204,6 +220,12 @@ class MigrationRunner
|
||||
$this->setGroup($group);
|
||||
}
|
||||
|
||||
// Sequential versions need adjusting to 3 places so they can be found later.
|
||||
if ($this->type === 'sequential')
|
||||
{
|
||||
$targetVersion = str_pad($targetVersion, 3, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
$migrations = $this->findMigrations();
|
||||
|
||||
if (empty($migrations))
|
||||
@ -284,6 +306,8 @@ class MigrationRunner
|
||||
*/
|
||||
public function latest(string $namespace = null, string $group = null)
|
||||
{
|
||||
$this->ensureTable();
|
||||
|
||||
// Set Namespace if not null
|
||||
if (! is_null($namespace))
|
||||
{
|
||||
@ -315,6 +339,8 @@ class MigrationRunner
|
||||
*/
|
||||
public function latestAll(string $group = null)
|
||||
{
|
||||
$this->ensureTable();
|
||||
|
||||
// Set database group if not null
|
||||
if (! is_null($group))
|
||||
{
|
||||
@ -322,7 +348,7 @@ class MigrationRunner
|
||||
}
|
||||
|
||||
// Get all namespaces form PSR4 paths.
|
||||
$config = new Autoload();
|
||||
$config = config('Autoload');
|
||||
$namespaces = $config->psr4;
|
||||
|
||||
foreach ($namespaces as $namespace => $path)
|
||||
@ -361,6 +387,8 @@ class MigrationRunner
|
||||
*/
|
||||
public function current(string $group = null)
|
||||
{
|
||||
$this->ensureTable();
|
||||
|
||||
// Set database group if not null
|
||||
if (! is_null($group))
|
||||
{
|
||||
@ -380,33 +408,59 @@ class MigrationRunner
|
||||
public function findMigrations()
|
||||
{
|
||||
$migrations = [];
|
||||
// Get namespace location form PSR4 paths.
|
||||
$config = new Autoload();
|
||||
helper('filesystem');
|
||||
|
||||
$location = $config->psr4[$this->namespace];
|
||||
// If $this->path contains a valid directory use it.
|
||||
if (! empty($this->path))
|
||||
{
|
||||
$dir = rtrim($this->path, DIRECTORY_SEPARATOR) . '/';
|
||||
}
|
||||
// Otherwise, get namespace location form PSR4 paths
|
||||
// and add Database/Migrations for a standard loation.
|
||||
else
|
||||
{
|
||||
$config = config('Autoload');
|
||||
|
||||
// Setting migration directories.
|
||||
$dir = rtrim($location, DIRECTORY_SEPARATOR) . '/Database/Migrations/';
|
||||
$location = $config->psr4[$this->namespace];
|
||||
|
||||
// Setting migration directories.
|
||||
$dir = rtrim($location, DIRECTORY_SEPARATOR) . '/Database/Migrations/';
|
||||
}
|
||||
|
||||
// Load all *_*.php files in the migrations path
|
||||
foreach (glob($dir . '*_*.php') as $file)
|
||||
// We can't use glob if we want it to be testable....
|
||||
$files = get_filenames($dir, true);
|
||||
|
||||
foreach ($files as $file)
|
||||
{
|
||||
if (substr($file, -4) !== '.php')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove the extension
|
||||
$name = basename($file, '.php');
|
||||
|
||||
// Filter out non-migration files
|
||||
if (preg_match($this->regex, $name))
|
||||
{
|
||||
// Create migration object using stdClass
|
||||
$migration = new \stdClass();
|
||||
|
||||
// Get migration version number
|
||||
$migration->version = $this->getMigrationNumber($name);
|
||||
$migration->name = $this->getMigrationName($name);
|
||||
$migration->path = $file;
|
||||
$migration->path = ! empty($this->path) && strpos($file, $this->path) !== 0
|
||||
? $this->path . $file
|
||||
: $file;
|
||||
|
||||
// Add to migrations[version]
|
||||
$migrations[$migration->version] = $migration;
|
||||
}
|
||||
}
|
||||
|
||||
ksort($migrations);
|
||||
|
||||
return $migrations;
|
||||
}
|
||||
|
||||
@ -436,7 +490,7 @@ class MigrationRunner
|
||||
}
|
||||
|
||||
// Check if $targetversion file is found
|
||||
if ($targetversion !== '0' && ! array_key_exists($targetversion, $migrations))
|
||||
if ((int)$targetversion !== 0 && ! array_key_exists($targetversion, $migrations))
|
||||
{
|
||||
if ($this->silent)
|
||||
{
|
||||
@ -458,14 +512,14 @@ class MigrationRunner
|
||||
{
|
||||
if ($this->type === 'sequential' && abs($migration->version - $loop) > 1)
|
||||
{
|
||||
throw new \RuntimeException(lang('Migration.gap') . ' ' . $migration->version);
|
||||
throw new \RuntimeException(lang('Migrations.gap') . ' ' . $migration->version);
|
||||
}
|
||||
// Check if all old migration files are all available to do downgrading
|
||||
if ($method === 'down')
|
||||
{
|
||||
if ($loop <= $history_size && $history_migrations[$loop]['version'] !== $migration->version)
|
||||
{
|
||||
throw new \RuntimeException(lang('Migration.gap') . ' ' . $migration->version);
|
||||
throw new \RuntimeException(lang('Migrations.gap') . ' ' . $migration->version);
|
||||
}
|
||||
}
|
||||
$loop ++;
|
||||
@ -476,6 +530,22 @@ class MigrationRunner
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sets the path to the base directory that will be used
|
||||
* when locating migrations. If left null, the value will
|
||||
* be chosen from $this->namespace's directory.
|
||||
*
|
||||
* @param string|null $path
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPath(string $path = null)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set namespace.
|
||||
* Allows other scripts to modify on the fly as needed.
|
||||
@ -514,10 +584,14 @@ class MigrationRunner
|
||||
* Set migration Name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return \CodeIgniter\Database\MigrationRunner
|
||||
*/
|
||||
public function setName(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -531,6 +605,8 @@ class MigrationRunner
|
||||
*/
|
||||
public function getHistory(string $group = 'default')
|
||||
{
|
||||
$this->ensureTable();
|
||||
|
||||
$query = $this->db->table($this->table)
|
||||
->where('group', $group)
|
||||
->where('namespace', $this->namespace)
|
||||
@ -602,6 +678,8 @@ class MigrationRunner
|
||||
*/
|
||||
protected function getVersion()
|
||||
{
|
||||
$this->ensureTable();
|
||||
|
||||
$row = $this->db->table($this->table)
|
||||
->select('version')
|
||||
->where('group', $this->group)
|
||||
@ -675,14 +753,14 @@ class MigrationRunner
|
||||
* Ensures that we have created our migrations table
|
||||
* in the database.
|
||||
*/
|
||||
protected function ensureTable()
|
||||
public function ensureTable()
|
||||
{
|
||||
if ($this->db->tableExists($this->table))
|
||||
if ($this->tableChecked || $this->db->tableExists($this->table))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$forge = \Config\Database::forge();
|
||||
$forge = \Config\Database::forge($this->db);
|
||||
|
||||
$forge->addField([
|
||||
'version' => [
|
||||
@ -713,6 +791,8 @@ class MigrationRunner
|
||||
]);
|
||||
|
||||
$forge->createTable($this->table, true);
|
||||
|
||||
$this->tableChecked = true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
@ -105,7 +105,7 @@ class Builder extends BaseBuilder
|
||||
|
||||
$sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') + {$value}"]);
|
||||
|
||||
return $this->db->query($sql, $this->binds);
|
||||
return $this->db->query($sql, $this->binds, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -124,7 +124,7 @@ class Builder extends BaseBuilder
|
||||
|
||||
$sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') - {$value}"]);
|
||||
|
||||
return $this->db->query($sql, $this->binds);
|
||||
return $this->db->query($sql, $this->binds, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -162,7 +162,14 @@ class Builder extends BaseBuilder
|
||||
|
||||
$table = $this->QBFrom[0];
|
||||
|
||||
$set = $this->binds;
|
||||
$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);
|
||||
|
||||
|
@ -129,17 +129,32 @@ class Query implements QueryInterface
|
||||
/**
|
||||
* Sets the raw query string to use for this statement.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $binds
|
||||
* @param string $sql
|
||||
* @param array $binds
|
||||
* @param boolean $setEscape
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setQuery(string $sql, $binds = null)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -407,19 +422,18 @@ class Query implements QueryInterface
|
||||
|
||||
foreach ($binds as $placeholder => $value)
|
||||
{
|
||||
$escapedValue = $this->db->escape($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))
|
||||
if (is_array($value[0]))
|
||||
{
|
||||
$escapedValue = '(' . implode(',', $escapedValue) . ')';
|
||||
}
|
||||
|
||||
$replacers[":{$placeholder}:"] = $escapedValue;
|
||||
|
||||
// $sql = preg_replace('|:' . $placeholder . '(?!\w)|', $escapedValue, $sql);
|
||||
}
|
||||
|
||||
$sql = strtr($sql, $replacers);
|
||||
@ -460,7 +474,7 @@ class Query implements QueryInterface
|
||||
do
|
||||
{
|
||||
$c --;
|
||||
$escapedValue = $this->db->escape($binds[$c]);
|
||||
$escapedValue = $binds[$c][1] ? $this->db->escape($binds[$c][0]) : $binds[$c[0]];
|
||||
if (is_array($escapedValue))
|
||||
{
|
||||
$escapedValue = '(' . implode(',', $escapedValue) . ')';
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 British Columbia Institute of Technology
|
||||
* 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
|
||||
@ -29,7 +29,7 @@
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2014-2017 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @copyright 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 British Columbia Institute of Technology
|
||||
* 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
|
||||
@ -29,7 +29,7 @@
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2014-2017 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @copyright 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
@ -307,6 +307,7 @@ class Connection extends BaseConnection implements ConnectionInterface
|
||||
throw new DatabaseException(lang('Database.failGetFieldData'));
|
||||
}
|
||||
$query = $query->getResultObject();
|
||||
|
||||
if (empty($query))
|
||||
{
|
||||
return [];
|
||||
@ -319,7 +320,8 @@ class Connection extends BaseConnection implements ConnectionInterface
|
||||
$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) ? (int)$query[$i]->pk : 0;
|
||||
$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;
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 British Columbia Institute of Technology
|
||||
* 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
|
||||
@ -29,7 +29,7 @@
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2014-2017 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @copyright 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
@ -152,12 +152,29 @@ class Forge extends \CodeIgniter\Database\Forge
|
||||
*/
|
||||
protected function _alterTable($alter_type, $table, $field)
|
||||
{
|
||||
if (in_array($alter_type, ['DROP', 'CHANGE'], true))
|
||||
switch ($alter_type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case 'DROP':
|
||||
$sqlTable = new Table($this->db, $this);
|
||||
|
||||
return parent::_alterTable($alter_type, $table, $field);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 British Columbia Institute of Technology
|
||||
* 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
|
||||
@ -29,7 +29,7 @@
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2014-2017 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @copyright 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 British Columbia Institute of Technology
|
||||
* 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
|
||||
@ -29,7 +29,7 @@
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2014-2017 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @copyright 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
|
343
system/Database/SQLite3/Table.php
Normal file
343
system/Database/SQLite3/Table.php
Normal file
@ -0,0 +1,343 @@
|
||||
<?php 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;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* @var Forge
|
||||
*/
|
||||
protected $forge;
|
||||
|
||||
/**
|
||||
* Table constructor.
|
||||
*
|
||||
* @param Connection $db
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the new table based on our current fields.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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 array
|
||||
*/
|
||||
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 $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.
|
||||
*/
|
||||
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}'");
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
* This content is released under the MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 British Columbia Institute of Technology
|
||||
* 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
|
||||
@ -29,7 +29,7 @@
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2014-2017 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @copyright 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
|
@ -154,6 +154,9 @@ class Seeder
|
||||
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;
|
||||
|
@ -37,6 +37,7 @@
|
||||
*/
|
||||
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use Config\Paths;
|
||||
|
||||
/**
|
||||
* Exceptions manager
|
||||
@ -259,7 +260,8 @@ class Exceptions
|
||||
$path = $this->viewPath;
|
||||
if (empty($path))
|
||||
{
|
||||
$path = APPPATH . 'Views/errors/';
|
||||
$paths = new Paths();
|
||||
$path = $paths->viewDirectory . '/errors/';
|
||||
}
|
||||
|
||||
$path = is_cli()
|
||||
|
@ -84,15 +84,8 @@ class Logs extends BaseCollector
|
||||
*/
|
||||
public function display(): array
|
||||
{
|
||||
$logs = $this->collectLogs();
|
||||
|
||||
if (empty($logs) || ! is_array($logs))
|
||||
{
|
||||
$logs = [];
|
||||
}
|
||||
|
||||
return [
|
||||
'logs' => $logs,
|
||||
'logs' => $this->collectLogs(),
|
||||
];
|
||||
}
|
||||
|
||||
@ -131,11 +124,10 @@ class Logs extends BaseCollector
|
||||
{
|
||||
if (! is_null($this->data))
|
||||
{
|
||||
return;
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
$logger = Services::logger(true);
|
||||
$this->data = $logger->logCache;
|
||||
return $this->data = Services::logger(true)->logCache ?? [];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php namespace CodeIgniter;
|
||||
|
||||
use CodeIgniter\I18n\Time;
|
||||
use CodeIgniter\Exceptions\CastException;
|
||||
|
||||
/**
|
||||
* CodeIgniter
|
||||
@ -78,7 +79,7 @@ class Entity
|
||||
protected $_original = [];
|
||||
|
||||
/**
|
||||
* Holds info whenever prperties have to be casted
|
||||
* Holds info whenever properties have to be casted
|
||||
*
|
||||
* @var boolean
|
||||
**/
|
||||
@ -170,7 +171,7 @@ class Entity
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($onlyChanged && $this->_original[$key] === null && $value === null)
|
||||
if ($onlyChanged && ! $this->hasPropertyChanged($key, $value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -192,6 +193,54 @@ class Entity
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Converts the properties of this class into an array. Unlike toArray()
|
||||
* this will not cast the data or use any magic accessors. It simply
|
||||
* returns the raw data for use when saving to the model, etc.
|
||||
*
|
||||
* @param boolean $onlyChanged
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toRawArray(bool $onlyChanged = false): array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
$properties = get_object_vars($this);
|
||||
|
||||
foreach ($properties as $key => $value)
|
||||
{
|
||||
if (substr($key, 0, 1) === '_')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($onlyChanged && ! $this->hasPropertyChanged($key, $value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$return[$key] = $this->$key;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks a property to see if it has changed since the entity was created.
|
||||
*
|
||||
* @param string $key
|
||||
* @param null $value
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function hasPropertyChanged(string $key, $value = null)
|
||||
{
|
||||
return ! (($this->_original[$key] === null && $value === null) || $this->_original[$key] === $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to allow retrieval of protected and private
|
||||
* class properties either by their name, or through a `getCamelCasedProperty()`
|
||||
@ -267,24 +316,37 @@ class Entity
|
||||
$value = $this->mutateDate($value);
|
||||
}
|
||||
|
||||
// Array casting requires that we serialize the value
|
||||
// when setting it so that it can easily be stored
|
||||
// back to the database.
|
||||
if (array_key_exists($key, $this->_options['casts']) && $this->_options['casts'][$key] === 'array')
|
||||
$isNullable = false;
|
||||
$castTo = false;
|
||||
|
||||
if (array_key_exists($key, $this->_options['casts']))
|
||||
{
|
||||
$value = serialize($value);
|
||||
$isNullable = substr($this->_options['casts'][$key], 0, 1) === '?';
|
||||
$castTo = $isNullable ? substr($this->_options['casts'][$key], 1) : $this->_options['casts'][$key];
|
||||
}
|
||||
|
||||
// JSON casting requires that we JSONize the value
|
||||
// when setting it so that it can easily be stored
|
||||
// back to the database.
|
||||
if (function_exists('json_encode') && array_key_exists($key, $this->_options['casts']) && ($this->_options['casts'][$key] === 'json' || $this->_options['casts'][$key] === 'json-array'))
|
||||
if (! $isNullable || ! is_null($value))
|
||||
{
|
||||
$value = json_encode($value);
|
||||
// Array casting requires that we serialize the value
|
||||
// when setting it so that it can easily be stored
|
||||
// back to the database.
|
||||
if ($castTo === 'array')
|
||||
{
|
||||
$value = serialize($value);
|
||||
}
|
||||
|
||||
// JSON casting requires that we JSONize the value
|
||||
// when setting it so that it can easily be stored
|
||||
// back to the database.
|
||||
if (($castTo === 'json' || $castTo === 'json-array') && function_exists('json_encode'))
|
||||
{
|
||||
$value = json_encode($value);
|
||||
}
|
||||
}
|
||||
|
||||
// if a set* method exists for this key,
|
||||
// use that method to insert this value.
|
||||
// *) should be outside $isNullable check - SO maybe wants to do sth with null value automatically
|
||||
$method = 'set' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $key)));
|
||||
if (method_exists($this, $method))
|
||||
{
|
||||
@ -428,13 +490,13 @@ class Entity
|
||||
|
||||
protected function castAs($value, string $type)
|
||||
{
|
||||
if(substr($type,0,1) === '?')
|
||||
if (substr($type, 0, 1) === '?')
|
||||
{
|
||||
if($value === null)
|
||||
if ($value === null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
$type = substr($type,1);
|
||||
$type = substr($type, 1);
|
||||
}
|
||||
|
||||
switch($type)
|
||||
|
@ -14,11 +14,6 @@ class ConfigException extends CriticalError
|
||||
*/
|
||||
protected $code = 3;
|
||||
|
||||
public static function forMissingMigrationsTable()
|
||||
{
|
||||
throw new static(lang('Migrations.missingTable'));
|
||||
}
|
||||
|
||||
public static function forInvalidMigrationType(string $type = null)
|
||||
{
|
||||
throw new static(lang('Migrations.invalidType', [$type]));
|
||||
|
100
system/Filters/CSRF.php
Normal file
100
system/Filters/CSRF.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php namespace CodeIgniter\Filters;
|
||||
|
||||
/**
|
||||
* 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 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
use CodeIgniter\Filters\FilterInterface;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use CodeIgniter\Security\Exceptions\SecurityException;
|
||||
use Config\Services;
|
||||
|
||||
class CSRF implements FilterInterface
|
||||
{
|
||||
/**
|
||||
* Do whatever processing this filter needs to do.
|
||||
* By default it should not return anything during
|
||||
* normal execution. However, when an abnormal state
|
||||
* is found, it should return an instance of
|
||||
* CodeIgniter\HTTP\Response. If it does, script
|
||||
* execution will end and that Response will be
|
||||
* sent back to the client, allowing for error pages,
|
||||
* redirects, etc.
|
||||
*
|
||||
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function before(RequestInterface $request)
|
||||
{
|
||||
if ($request->isCLI())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$security = Services::security();
|
||||
|
||||
try
|
||||
{
|
||||
$security->CSRFVerify($request);
|
||||
}
|
||||
catch (SecurityException $e)
|
||||
{
|
||||
if (config('App')->CSRFRedirect && ! $request->isAJAX())
|
||||
{
|
||||
return redirect()->back()->with('error', $e->getMessage());
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* We don't have anything to do here.
|
||||
*
|
||||
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
|
||||
* @param ResponseInterface|\CodeIgniter\HTTP\Response $response
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function after(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
74
system/Filters/DebugToolbar.php
Normal file
74
system/Filters/DebugToolbar.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php namespace CodeIgniter\Filters;
|
||||
|
||||
/**
|
||||
* 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 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
use CodeIgniter\Filters\FilterInterface;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Config\Services;
|
||||
|
||||
class DebugToolbar implements FilterInterface
|
||||
{
|
||||
/**
|
||||
* We don't need to do anything here.
|
||||
*
|
||||
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function before(RequestInterface $request)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* If the debug flag is set (CI_DEBUG) then collect performance
|
||||
* and debug information and display it in a toolbar.
|
||||
*
|
||||
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
|
||||
* @param ResponseInterface|\CodeIgniter\HTTP\Response $response
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function after(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
Services::toolbar()->prepare();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
}
|
@ -119,7 +119,7 @@ class Filters
|
||||
*/
|
||||
public function run(string $uri, $position = 'before')
|
||||
{
|
||||
$this->initialize($uri);
|
||||
$this->initialize(strtolower($uri));
|
||||
|
||||
foreach ($this->filters[$position] as $alias => $rules)
|
||||
{
|
||||
@ -350,7 +350,7 @@ class Filters
|
||||
foreach ($rules as $path)
|
||||
{
|
||||
// Prep it for regex
|
||||
$path = str_replace('/*', '*', $path);
|
||||
$path = strtolower(str_replace('/*', '*', $path));
|
||||
$path = trim(str_replace('*', '.+', $path), '/ ');
|
||||
|
||||
// Path doesn't match the URI? continue on...
|
||||
@ -388,7 +388,7 @@ class Filters
|
||||
foreach ($rules as $path)
|
||||
{
|
||||
// Prep it for regex
|
||||
$path = str_replace('/*', '*', $path);
|
||||
$path = strtolower(str_replace('/*', '*', $path));
|
||||
$path = trim(str_replace('*', '.+', $path), '/ ');
|
||||
|
||||
// Path doesn't match the URI? continue on...
|
||||
@ -434,7 +434,7 @@ class Filters
|
||||
return;
|
||||
}
|
||||
|
||||
$uri = trim($uri, '/ ');
|
||||
$uri = strtolower(trim($uri, '/ '));
|
||||
|
||||
$matches = [];
|
||||
|
||||
@ -446,7 +446,7 @@ class Filters
|
||||
foreach ($settings['before'] as $path)
|
||||
{
|
||||
// Prep it for regex
|
||||
$path = str_replace('/*', '*', $path);
|
||||
$path = strtolower(str_replace('/*', '*', $path));
|
||||
$path = trim(str_replace('*', '.+', $path), '/ ');
|
||||
|
||||
if (preg_match('#' . $path . '#', $uri) !== 1)
|
||||
@ -467,7 +467,7 @@ class Filters
|
||||
foreach ($settings['after'] as $path)
|
||||
{
|
||||
// Prep it for regex
|
||||
$path = str_replace('/*', '*', $path);
|
||||
$path = strtolower(str_replace('/*', '*', $path));
|
||||
$path = trim(str_replace('*', '.+', $path), '/ ');
|
||||
|
||||
if (preg_match('#' . $path . '#', $uri) !== 1)
|
||||
@ -479,6 +479,7 @@ class Filters
|
||||
}
|
||||
|
||||
$this->filters['after'] = array_merge($this->filters['after'], $matches);
|
||||
$matches = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
78
system/Filters/Honeypot.php
Normal file
78
system/Filters/Honeypot.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php namespace CodeIgniter\Filters;
|
||||
|
||||
/**
|
||||
* 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 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
use CodeIgniter\Filters\FilterInterface;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Config\Services;
|
||||
use CodeIgniter\Honeypot\Exceptions\HoneypotException;
|
||||
|
||||
class Honeypot implements FilterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Checks if Honeypot field is empty; if not
|
||||
* then the requester is a bot
|
||||
*
|
||||
* @param CodeIgniter\HTTP\RequestInterface $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function before(RequestInterface $request)
|
||||
{
|
||||
$honeypot = Services::honeypot(new \Config\Honeypot());
|
||||
if ($honeypot->hasContent($request))
|
||||
{
|
||||
throw HoneypotException::isBot();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a honypot to the current response.
|
||||
*
|
||||
* @param CodeIgniter\HTTP\RequestInterface $request
|
||||
* @param CodeIgniter\HTTP\ResponseInterface $response
|
||||
* @return mixed
|
||||
*/
|
||||
public function after(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$honeypot = Services::honeypot(new \Config\Honeypot());
|
||||
$honeypot->attachHoneypot($response);
|
||||
}
|
||||
|
||||
}
|
@ -683,7 +683,7 @@ class ContentSecurityPolicy
|
||||
|
||||
$this->styleSrc[] = 'nonce-' . $nonce;
|
||||
|
||||
return "nonce={$nonce}";
|
||||
return "nonce=\"{$nonce}\"";
|
||||
}, $body
|
||||
);
|
||||
|
||||
@ -694,7 +694,7 @@ class ContentSecurityPolicy
|
||||
|
||||
$this->scriptSrc[] = 'nonce-' . $nonce;
|
||||
|
||||
return "nonce={$nonce}";
|
||||
return "nonce=\"{$nonce}\"";
|
||||
}, $body
|
||||
);
|
||||
|
||||
@ -799,12 +799,6 @@ class ContentSecurityPolicy
|
||||
*/
|
||||
protected function addToHeader(string $name, $values = null)
|
||||
{
|
||||
if (empty($values))
|
||||
{
|
||||
$this->tempHeaders[$name] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_string($values))
|
||||
{
|
||||
$values = [$values => 0];
|
||||
|
@ -265,20 +265,20 @@ class UploadedFile extends File implements UploadedFileInterface
|
||||
*/
|
||||
public function getErrorString()
|
||||
{
|
||||
static $errors = [
|
||||
UPLOAD_ERR_OK => 'The file uploaded with success.',
|
||||
UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive.',
|
||||
UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.',
|
||||
UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.',
|
||||
UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
|
||||
UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.',
|
||||
UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.',
|
||||
UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.',
|
||||
$errors = [
|
||||
UPLOAD_ERR_OK => lang('HTTP.uploadErrOk'),
|
||||
UPLOAD_ERR_INI_SIZE => lang('HTTP.uploadErrIniSize'),
|
||||
UPLOAD_ERR_FORM_SIZE => lang('HTTP.uploadErrFormSize'),
|
||||
UPLOAD_ERR_PARTIAL => lang('HTTP.uploadErrPartial'),
|
||||
UPLOAD_ERR_NO_FILE => lang('HTTP.uploadErrNoFile'),
|
||||
UPLOAD_ERR_CANT_WRITE => lang('HTTP.uploadErrCantWrite'),
|
||||
UPLOAD_ERR_NO_TMP_DIR => lang('HTTP.uploadErrNoTmpDir'),
|
||||
UPLOAD_ERR_EXTENSION => lang('HTTP.uploadErrExtension')
|
||||
];
|
||||
|
||||
$error = is_null($this->error) ? UPLOAD_ERR_OK : $this->error;
|
||||
|
||||
return sprintf($errors[$error] ?? 'The file "%s" was not uploaded due to an unknown error.', $this->getName());
|
||||
return sprintf($errors[$error] ?? lang('HTTP.uploadErrUnknown'), $this->getName());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
@ -91,7 +91,7 @@ class Header
|
||||
|
||||
/**
|
||||
* Gets the raw value of the header. This may return either a string
|
||||
* of an array, depending on whether the header has mutliple values or not.
|
||||
* of an array, depending on whether the header has multiple values or not.
|
||||
*
|
||||
* @return array|null|string
|
||||
*/
|
||||
|
@ -409,7 +409,7 @@ class Request extends Message implements RequestInterface
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($value))
|
||||
if (!isset($value))
|
||||
{
|
||||
$value = $this->globals[$method][$index] ?? null;
|
||||
}
|
||||
|
@ -760,7 +760,7 @@ class URI
|
||||
// URL Decode the value to protect
|
||||
// from double-encoding a URL.
|
||||
// Especially useful with the Pager.
|
||||
$parts[$key] = $this->decode($value);
|
||||
$parts[$this->decode($key)] = $this->decode($value);
|
||||
}
|
||||
|
||||
$this->query = $parts;
|
||||
|
@ -47,7 +47,7 @@ if (! function_exists('now'))
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
function now(string $timezone = null)
|
||||
function now(string $timezone = null): int
|
||||
{
|
||||
$timezone = empty($timezone) ? app_timezone() : $timezone;
|
||||
|
||||
|
@ -65,6 +65,12 @@ if (! function_exists('form_open'))
|
||||
$action = site_url($action);
|
||||
}
|
||||
|
||||
if(is_array($attributes) && array_key_exists('csrf_id', $attributes))
|
||||
{
|
||||
$csrfId = $attributes['csrf_id'];
|
||||
unset($attributes['csrf_id']);
|
||||
}
|
||||
|
||||
$attributes = stringify_attributes($attributes);
|
||||
|
||||
if (stripos($attributes, 'method=') === false)
|
||||
@ -82,17 +88,16 @@ if (! function_exists('form_open'))
|
||||
// Add CSRF field if enabled, but leave it out for GET requests and requests to external websites
|
||||
$before = Services::filters()->getFilters()['before'];
|
||||
|
||||
if ((in_array('csrf', $before) || array_key_exists('csrf', $before)) && strpos($action, base_url()) !== false && ! stripos($form, 'method="get"')
|
||||
)
|
||||
if ((in_array('csrf', $before) || array_key_exists('csrf', $before)) && strpos($action, base_url()) !== false && ! stripos($form, 'method="get"'))
|
||||
{
|
||||
$hidden[csrf_token()] = csrf_hash();
|
||||
$form .= csrf_field($csrfId ?? null);
|
||||
}
|
||||
|
||||
if (is_array($hidden))
|
||||
{
|
||||
foreach ($hidden as $name => $value)
|
||||
{
|
||||
$form .= '<input type="hidden" name="' . $name . '" value="' . esc($value, 'html') . '" style="display: none;" />' . "\n";
|
||||
$form .= form_hidden($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,7 +172,7 @@ if (! function_exists('form_hidden'))
|
||||
|
||||
if (! is_array($value))
|
||||
{
|
||||
$form .= '<input type="hidden" name="' . $name . '" value="' . esc($value, 'html') . "\" />\n";
|
||||
$form .= '<input type="hidden" name="' . $name . '" value="' . esc($value, 'html') . "\" style=\"display:none;\" />\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -156,10 +156,9 @@ if (! function_exists('img'))
|
||||
$src = ['src' => $src];
|
||||
}
|
||||
|
||||
//If there is no alt attribute defined, set it to an empty string.
|
||||
if (! isset($src['alt']))
|
||||
{
|
||||
$src['alt'] = '';
|
||||
$src['alt'] = $attributes['alt'] ?? '';
|
||||
}
|
||||
|
||||
$img = '<img';
|
||||
@ -184,6 +183,12 @@ if (! function_exists('img'))
|
||||
}
|
||||
}
|
||||
|
||||
// prevent passing "alt" to stringify_attributes
|
||||
if (is_array($attributes) && isset($attributes['alt']))
|
||||
{
|
||||
unset($attributes['alt']);
|
||||
}
|
||||
|
||||
return $img . stringify_attributes($attributes) . ' />';
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ if (! function_exists('number_to_size'))
|
||||
* @param integer $precision
|
||||
* @param string $locale
|
||||
*
|
||||
* @return string
|
||||
* @return boolean|string
|
||||
*/
|
||||
function number_to_size($num, int $precision = 1, string $locale = null)
|
||||
{
|
||||
@ -178,7 +178,7 @@ if (! function_exists('number_to_currency'))
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function number_to_currency($num, string $currency, string $locale = null)
|
||||
function number_to_currency($num, string $currency, string $locale = null): string
|
||||
{
|
||||
return format_number($num, 1, $locale, [
|
||||
'type' => NumberFormatter::CURRENCY,
|
||||
@ -202,7 +202,7 @@ if (! function_exists('format_number'))
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function format_number($num, int $precision = 1, string $locale = null, array $options = [])
|
||||
function format_number($num, int $precision = 1, string $locale = null, array $options = []): string
|
||||
{
|
||||
// Locale is either passed in here, negotiated with client, or grabbed from our config file.
|
||||
$locale = $locale ?? \CodeIgniter\Config\Services::request()->getLocale();
|
||||
@ -259,14 +259,14 @@ if (! function_exists('number_to_roman'))
|
||||
*
|
||||
* @param integer $num it will convert to int
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
function number_to_roman($num)
|
||||
{
|
||||
$num = (int) $num;
|
||||
if ($num < 1 || $num > 3999)
|
||||
{
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$_number_to_roman = function ($num, $th) use (&$_number_to_roman) {
|
||||
|
@ -45,7 +45,7 @@ if (! function_exists('sanitize_filename'))
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function sanitize_filename(string $filename)
|
||||
function sanitize_filename(string $filename): string
|
||||
{
|
||||
return Services::security()->sanitizeFilename($filename);
|
||||
}
|
||||
@ -61,7 +61,7 @@ if (! function_exists('strip_image_tags'))
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
function strip_image_tags(string $str)
|
||||
function strip_image_tags(string $str): string
|
||||
{
|
||||
return preg_replace([
|
||||
'#<img[\s/]+.*?src\s*=\s*(["\'])([^\\1]+?)\\1.*?\>#i',
|
||||
|
@ -150,7 +150,7 @@ if (! function_exists('ascii_to_entities'))
|
||||
{
|
||||
/*
|
||||
If the $temp array has a value but we have moved on, then it seems only
|
||||
fair that we output that entity and restart $temp before continuing. -Paul
|
||||
fair that we output that entity and restart $temp before continuing.
|
||||
*/
|
||||
if (count($temp) === 1)
|
||||
{
|
||||
@ -281,7 +281,7 @@ if (! function_exists('word_censor'))
|
||||
// \w, \b and a few others do not match on a unicode character
|
||||
// set for performance reasons. As a result words like über
|
||||
// will not match on a word boundary. Instead, we'll assume that
|
||||
// a bad word will be bookeneded by any of these characters.
|
||||
// a bad word will be bookended by any of these characters.
|
||||
$delim = '[-_\'\"`(){}<>\[\]|!?@#%&,.:;^~*+=\/ 0-9\n\r\t]';
|
||||
|
||||
foreach ($censored as $badword)
|
||||
@ -402,7 +402,7 @@ if (! function_exists('highlight_phrase'))
|
||||
*
|
||||
* @param string $str the text string
|
||||
* @param string $phrase the phrase you'd like to highlight
|
||||
* @param string $tag_open the openging tag to precede the phrase with
|
||||
* @param string $tag_open the opening tag to precede the phrase with
|
||||
* @param string $tag_close the closing tag to end the phrase with
|
||||
*
|
||||
* @return string
|
||||
@ -460,7 +460,7 @@ if (! function_exists('word_wrap'))
|
||||
* Anything placed between {unwrap}{/unwrap} will not be word wrapped, nor
|
||||
* will URLs.
|
||||
*
|
||||
* @param string $str the text string
|
||||
* @param string $str the text string
|
||||
* @param integer $charlim = 76 the number of characters to wrap at
|
||||
*
|
||||
* @return string
|
||||
@ -604,9 +604,9 @@ if (! function_exists('strip_slashes'))
|
||||
*
|
||||
* Removes slashes contained in a string or in an array
|
||||
*
|
||||
* @param mixed string or array
|
||||
* @param mixed $str string or array
|
||||
*
|
||||
* @return mixed string or array
|
||||
* @return mixed string or array
|
||||
*/
|
||||
function strip_slashes($str)
|
||||
{
|
||||
@ -632,7 +632,7 @@ if (! function_exists('strip_quotes'))
|
||||
*
|
||||
* Removes single and double quotes from a string
|
||||
*
|
||||
* @param string
|
||||
* @param string $str
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -651,7 +651,7 @@ if (! function_exists('quotes_to_entities'))
|
||||
*
|
||||
* Converts single and double quotes to entities
|
||||
*
|
||||
* @param string
|
||||
* @param string $str
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -677,7 +677,7 @@ if (! function_exists('reduce_double_slashes'))
|
||||
*
|
||||
* http://www.some-site.com/index.php
|
||||
*
|
||||
* @param string
|
||||
* @param string $str
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -712,7 +712,7 @@ if (! function_exists('reduce_multiples'))
|
||||
{
|
||||
$str = preg_replace('#' . preg_quote($character, '#') . '{2,}#', $character, $str);
|
||||
|
||||
return ($trim === true) ? trim($str, $character) : $str;
|
||||
return ($trim) ? trim($str, $character) : $str;
|
||||
}
|
||||
}
|
||||
|
||||
@ -814,7 +814,7 @@ if (! function_exists('alternator'))
|
||||
|
||||
$args = func_get_args();
|
||||
|
||||
return $args[($i ++ % count($args))];
|
||||
return $args[($i++ % count($args))];
|
||||
}
|
||||
}
|
||||
|
||||
@ -829,13 +829,13 @@ if (! function_exists('excerpt'))
|
||||
*
|
||||
* @param string $text String to search the phrase
|
||||
* @param string $phrase Phrase that will be searched for.
|
||||
* @param integer $radius The amount of characters returned arround the phrase.
|
||||
* @param integer $radius The amount of characters returned around the phrase.
|
||||
* @param string $ellipsis Ending that will be appended
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* If no $phrase is passed, will generate an excerpt of $radius characters
|
||||
* from the begining of $text.
|
||||
* from the beginning of $text.
|
||||
*/
|
||||
function excerpt(string $text, string $phrase = null, int $radius = 100, string $ellipsis = '...'): string
|
||||
{
|
||||
|
@ -334,7 +334,7 @@ if (! function_exists('mailto'))
|
||||
*/
|
||||
function mailto($email, string $title = '', $attributes = ''): string
|
||||
{
|
||||
if ($title === '')
|
||||
if (trim($title) === '')
|
||||
{
|
||||
$title = $email;
|
||||
}
|
||||
@ -360,7 +360,7 @@ if (! function_exists('safe_mailto'))
|
||||
*/
|
||||
function safe_mailto($email, string $title = '', $attributes = ''): string
|
||||
{
|
||||
if ($title === '')
|
||||
if (trim($title) === '')
|
||||
{
|
||||
$title = $email;
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ if (! function_exists('xml_convert'))
|
||||
''',
|
||||
'-',
|
||||
];
|
||||
$str = str_replace($original, $replacements, $str);
|
||||
$str = str_replace($original, $replacement, $str);
|
||||
|
||||
// Decode the temp markers back to entities
|
||||
$str = preg_replace('/' . $temp . '(\d+);/', '&#\\1;', $str);
|
||||
|
@ -190,17 +190,6 @@ class Language
|
||||
*/
|
||||
protected function parseLine(string $line, string $locale): array
|
||||
{
|
||||
// If there's no possibility of a filename being in the string
|
||||
// simply return the string, and they can parse the replacement
|
||||
// without it being in a file.
|
||||
if (strpos($line, '.') === false)
|
||||
{
|
||||
return [
|
||||
null,
|
||||
$line,
|
||||
];
|
||||
}
|
||||
|
||||
$file = substr($line, 0, strpos($line, '.'));
|
||||
$line = substr($line, strlen($file) + 1);
|
||||
|
||||
|
@ -24,4 +24,5 @@ return [
|
||||
'failGetForeignKeyData' => 'Failed to get foreign key data from database.',
|
||||
'parseStringFail' => 'Parsing key string failed.',
|
||||
'featureUnavailable' => 'This feature is not available for the database you are using.',
|
||||
'tableNotFound' => 'Table `{0}` was not found in the current database.',
|
||||
];
|
||||
|
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Email language strings.
|
||||
*
|
||||
* @package CodeIgniter
|
||||
* @author CodeIgniter Dev Team
|
||||
* @copyright 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
* @filesource
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
|
||||
return [
|
||||
'mustBeArray' => 'The email validation method must be passed an array.',
|
||||
'invalidAddress' => 'Invalid email address: {0}',
|
||||
'attachmentMissing' => 'Unable to locate the following email attachment: {0}',
|
||||
'attachmentUnreadable' => 'Unable to open this attachment: {0}',
|
||||
'noFrom' => 'Cannot send mail with no "From" header.',
|
||||
'noRecipients' => 'You must include recipients: To, Cc, or Bcc',
|
||||
'sendFailurePHPMail' => 'Unable to send email using PHP mail(). Your server might not be configured to send mail using this method.',
|
||||
'sendFailureSendmail' => 'Unable to send email using PHP Sendmail. Your server might not be configured to send mail using this method.',
|
||||
'sendFailureSmtp' => 'Unable to send email using PHP SMTP. Your server might not be configured to send mail using this method.',
|
||||
'sent' => 'Your message has been successfully sent using the following protocol: {0, string}',
|
||||
'noSocket' => 'Unable to open a socket to Sendmail. Please check settings.',
|
||||
'noHostname' => 'You did not specify a SMTP hostname.',
|
||||
'SMTPError' => 'The following SMTP error was encountered: {0}',
|
||||
'noSMTPAuth' => 'Error: You must assign a SMTP username and password.',
|
||||
'failedSMTPLogin' => 'Failed to send AUTH LOGIN command. Error: {0}',
|
||||
'SMTPAuthUsername' => 'Failed to authenticate username. Error: {0}',
|
||||
'SMTPAuthPassword' => 'Failed to authenticate password. Error: {0}',
|
||||
'SMTPDataFailure' => 'Unable to send data: {0}',
|
||||
'exitStatus' => 'Exit status code: {0}',
|
||||
];
|
@ -64,4 +64,14 @@ return [
|
||||
'alreadyMoved' => 'The uploaded file has already been moved.',
|
||||
'invalidFile' => 'The original file is not a valid file.',
|
||||
'moveFailed' => 'Could not move file {0} to {1} ({2})',
|
||||
|
||||
'uploadErrOk' => 'The file uploaded with success.',
|
||||
'uploadErrIniSize' => 'The file "%s" exceeds your upload_max_filesize ini directive.',
|
||||
'uploadErrFormSize' => 'The file "%s" exceeds the upload limit defined in your form.',
|
||||
'uploadErrPartial' => 'The file "%s" was only partially uploaded.',
|
||||
'uploadErrNoFile' => 'No file was uploaded.',
|
||||
'uploadErrCantWrite' => 'The file "%s" could not be written on disk.',
|
||||
'uploadErrNoTmpDir' => 'File could not be uploaded: missing temporary directory.',
|
||||
'uploadErrExtension' => 'File upload was stopped by a PHP extension.',
|
||||
'uploadErrUnknown' => 'The file "%s" was not uploaded due to an unknown error.'
|
||||
];
|
||||
|
@ -14,7 +14,7 @@
|
||||
*/
|
||||
|
||||
return [
|
||||
'invalidCellMethod' => '{class}::{method} is not a valid method."',
|
||||
'invalidCellMethod' => '{class}::{method} is not a valid method.',
|
||||
'missingCellParameters' => '{class}::{method} has no params.',
|
||||
'invalidCellParameter' => '{0} is not a valid param name.',
|
||||
'noCellClass' => 'No view cell class provided.',
|
||||
|
@ -36,6 +36,8 @@
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
use CodeIgniter\Log\Exceptions\LogException;
|
||||
|
||||
/**
|
||||
* Log error messages to file system
|
||||
*/
|
||||
@ -138,7 +140,10 @@ class FileHandler extends BaseHandler implements HandlerInterface
|
||||
{
|
||||
if (($result = fwrite($fp, substr($msg, $written))) === false)
|
||||
{
|
||||
// if we get this far, we'll never see this during travis-ci
|
||||
// @codeCoverageIgnoreStart
|
||||
break;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,7 +511,7 @@ class Logger implements LoggerInterface
|
||||
* Cleans the paths of filenames by replacing APPPATH, SYSTEMPATH, FCPATH
|
||||
* with the actual var. i.e.
|
||||
*
|
||||
* /var/www/site/application/Controllers/Home.php
|
||||
* /var/www/site/app/Controllers/Home.php
|
||||
* becomes:
|
||||
* APPPATH/Controllers/Home.php
|
||||
*
|
||||
|
@ -149,7 +149,7 @@ class Model
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The column used for insert timestampes
|
||||
* The column used for insert timestamps
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
@ -343,8 +343,6 @@ class Model
|
||||
$this->tempReturnType = $this->returnType;
|
||||
$this->tempUseSoftDeletes = $this->useSoftDeletes;
|
||||
|
||||
$this->reset();
|
||||
|
||||
return $row['data'];
|
||||
}
|
||||
|
||||
@ -454,6 +452,7 @@ class Model
|
||||
* @param array|object $data
|
||||
*
|
||||
* @return boolean
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function save($data)
|
||||
{
|
||||
@ -462,7 +461,12 @@ class Model
|
||||
// them as an array.
|
||||
if (is_object($data) && ! $data instanceof \stdClass)
|
||||
{
|
||||
$data = static::classToArray($data, $this->dateFormat);
|
||||
$data = static::classToArray($data, $this->primaryKey, $this->dateFormat);
|
||||
}
|
||||
|
||||
if (empty($data))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_object($data) && isset($data->{$this->primaryKey}))
|
||||
@ -481,23 +485,28 @@ class Model
|
||||
return $response;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Takes a class an returns an array of it's public and protected
|
||||
* properties as an array suitable for use in creates and updates.
|
||||
*
|
||||
* @param string|object $data
|
||||
* @param string|null $primaryKey
|
||||
* @param string $dateFormat
|
||||
*
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public static function classToArray($data, string $dateFormat = 'datetime'): array
|
||||
public static function classToArray($data, $primaryKey = null, string $dateFormat = 'datetime'): array
|
||||
{
|
||||
if (method_exists($data, 'toArray'))
|
||||
if (method_exists($data, 'toRawArray'))
|
||||
{
|
||||
$properties = $data->toArray(true, false);
|
||||
$properties = $data->toRawArray(true);
|
||||
|
||||
// Always grab the primary key otherwise updates will fail.
|
||||
if (! empty($properties) && ! empty($primaryKey) && ! in_array($primaryKey, $properties))
|
||||
{
|
||||
$properties[$primaryKey] = $data->{$primaryKey};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -569,6 +578,7 @@ class Model
|
||||
* @param boolean $returnID Whether insert ID should be returned or not.
|
||||
*
|
||||
* @return integer|string|boolean
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function insert($data = null, bool $returnID = true)
|
||||
{
|
||||
@ -588,7 +598,7 @@ class Model
|
||||
// them as an array.
|
||||
if (is_object($data) && ! $data instanceof \stdClass)
|
||||
{
|
||||
$data = static::classToArray($data, $this->dateFormat);
|
||||
$data = static::classToArray($data, $this->primaryKey, $this->dateFormat);
|
||||
}
|
||||
|
||||
// If it's still a stdClass, go ahead and convert to
|
||||
@ -689,6 +699,7 @@ class Model
|
||||
* @param array|object $data
|
||||
*
|
||||
* @return boolean
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function update($id = null, $data = null)
|
||||
{
|
||||
@ -711,7 +722,7 @@ class Model
|
||||
// them as an array.
|
||||
if (is_object($data) && ! $data instanceof \stdClass)
|
||||
{
|
||||
$data = static::classToArray($data, $this->dateFormat);
|
||||
$data = static::classToArray($data, $this->primaryKey, $this->dateFormat);
|
||||
}
|
||||
|
||||
// If it's still a stdClass, go ahead and convert to
|
||||
@ -994,7 +1005,7 @@ class Model
|
||||
throw DataException::forEmptyDataset('chunk');
|
||||
}
|
||||
|
||||
$rows = $rows->getResult();
|
||||
$rows = $rows->getResult($this->tempReturnType);
|
||||
|
||||
$offset += $size;
|
||||
|
||||
@ -1254,27 +1265,63 @@ class Model
|
||||
$data = (array) $data;
|
||||
}
|
||||
|
||||
$rules = $this->validationRules;
|
||||
|
||||
// ValidationRules can be either a string, which is the group name,
|
||||
// or an array of rules.
|
||||
if (is_string($this->validationRules))
|
||||
if (is_string($rules))
|
||||
{
|
||||
$valid = $this->validation->run($data, $this->validationRules, $this->DBGroup);
|
||||
$rules = $this->validation->loadRuleGroup($rules);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Replace any placeholders (i.e. {id}) in the rules with
|
||||
// the value found in $data, if exists.
|
||||
$rules = $this->fillPlaceholders($this->validationRules, $data);
|
||||
|
||||
$this->validation->setRules($rules, $this->validationMessages);
|
||||
$valid = $this->validation->run($data, null, $this->DBGroup);
|
||||
$rules = $this->cleanValidationRules($rules, $data);
|
||||
|
||||
// If no data existed that needs validation
|
||||
// our job is done here.
|
||||
if (empty($rules))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Replace any placeholders (i.e. {id}) in the rules with
|
||||
// the value found in $data, if exists.
|
||||
$rules = $this->fillPlaceholders($rules, $data);
|
||||
|
||||
$this->validation->setRules($rules, $this->validationMessages);
|
||||
$valid = $this->validation->run($data, null, $this->DBGroup);
|
||||
|
||||
return (bool) $valid;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Removes any rules that apply to fields that have not been set
|
||||
* currently so that rules don't block updating when only updating
|
||||
* a partial row.
|
||||
*
|
||||
* @param array $rules
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function cleanValidationRules($rules, array $data = null)
|
||||
{
|
||||
if (empty($data))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($rules as $field => $rule)
|
||||
{
|
||||
if (! array_key_exists($field, $data))
|
||||
{
|
||||
unset($rules[$field]);
|
||||
}
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace any placeholders within the rules with the values that
|
||||
* match the 'key' of any properties being set. For example, if
|
||||
@ -1312,6 +1359,13 @@ class Model
|
||||
{
|
||||
foreach ($rule as &$row)
|
||||
{
|
||||
// Should only be an `errors` array
|
||||
// which doesn't take placeholders.
|
||||
if (is_array($row))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$row = strtr($row, $replacements);
|
||||
}
|
||||
continue;
|
||||
@ -1436,7 +1490,7 @@ class Model
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
if (in_array($name, ['primaryKey', 'table', 'returnType']))
|
||||
if (in_array($name, ['primaryKey', 'table', 'returnType', 'DBGroup']))
|
||||
{
|
||||
return $this->{$name};
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ class RouteCollection implements RouteCollectionInterface
|
||||
* Constructor
|
||||
*
|
||||
* @param FileLocator $locator
|
||||
* @param Config/Modules $moduleConfig
|
||||
* @param \Config\Modules $moduleConfig
|
||||
*/
|
||||
public function __construct(FileLocator $locator, $moduleConfig)
|
||||
{
|
||||
@ -393,7 +393,6 @@ class RouteCollection implements RouteCollectionInterface
|
||||
/**
|
||||
* Will attempt to discover any additional routes, either through
|
||||
* the local PSR4 namespaces, or through selected Composer packages.
|
||||
* (Composer coming soon...)
|
||||
*/
|
||||
protected function discoverRoutes()
|
||||
{
|
||||
@ -406,9 +405,6 @@ class RouteCollection implements RouteCollectionInterface
|
||||
// so route files can access it.
|
||||
$routes = $this;
|
||||
|
||||
/*
|
||||
* Discover Local Files
|
||||
*/
|
||||
if ($this->moduleConfig->shouldDiscover('routes'))
|
||||
{
|
||||
$files = $this->fileLocator->search('Config/Routes.php');
|
||||
@ -425,10 +421,6 @@ class RouteCollection implements RouteCollectionInterface
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Discover Composer files (coming soon)
|
||||
*/
|
||||
|
||||
$this->didDiscover = true;
|
||||
}
|
||||
|
||||
@ -871,17 +863,17 @@ class RouteCollection implements RouteCollectionInterface
|
||||
$this->delete($name . '/' . $id, $new_name . '::delete/$1', $options);
|
||||
}
|
||||
|
||||
// Web Safe?
|
||||
// Web Safe? delete needs checking before update because of method name
|
||||
if (isset($options['websafe']))
|
||||
{
|
||||
if (in_array('update', $methods))
|
||||
{
|
||||
$this->post($name . '/' . $id, $new_name . '::update/$1', $options);
|
||||
}
|
||||
if (in_array('delete', $methods))
|
||||
{
|
||||
$this->post($name . '/' . $id . '/delete', $new_name . '::delete/$1', $options);
|
||||
}
|
||||
if (in_array('update', $methods))
|
||||
{
|
||||
$this->post($name . '/' . $id, $new_name . '::update/$1', $options);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
@ -1240,7 +1232,8 @@ class RouteCollection implements RouteCollectionInterface
|
||||
*/
|
||||
protected function create(string $verb, string $from, $to, array $options = null)
|
||||
{
|
||||
$prefix = is_null($this->group) ? '' : $this->group . '/';
|
||||
$overwrite = false;
|
||||
$prefix = is_null($this->group) ? '' : $this->group . '/';
|
||||
|
||||
$from = filter_var($prefix . $from, FILTER_SANITIZE_STRING);
|
||||
|
||||
@ -1257,10 +1250,12 @@ class RouteCollection implements RouteCollectionInterface
|
||||
if (! empty($options['hostname']))
|
||||
{
|
||||
// @todo determine if there's a way to whitelist hosts?
|
||||
if (strtolower($_SERVER['HTTP_HOST']) !== strtolower($options['hostname']))
|
||||
if (isset($_SERVER['HTTP_HOST']) && strtolower($_SERVER['HTTP_HOST']) !== strtolower($options['hostname']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$overwrite = true;
|
||||
}
|
||||
|
||||
// Limiting to subdomains?
|
||||
@ -1272,6 +1267,8 @@ class RouteCollection implements RouteCollectionInterface
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$overwrite = true;
|
||||
}
|
||||
|
||||
// Are we offsetting the binds?
|
||||
@ -1316,11 +1313,11 @@ class RouteCollection implements RouteCollectionInterface
|
||||
$name = $options['as'] ?? $from;
|
||||
|
||||
// Don't overwrite any existing 'froms' so that auto-discovered routes
|
||||
// do not overwrite any application/Config/Routes settings. The app
|
||||
// do not overwrite any app/Config/Routes settings. The app
|
||||
// routes should always be the "source of truth".
|
||||
// this works only because discovered routes are added just prior
|
||||
// to attempting to route the request.
|
||||
if (isset($this->routes[$verb][$name]))
|
||||
if (isset($this->routes[$verb][$name]) && ! $overwrite)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -1350,6 +1347,12 @@ class RouteCollection implements RouteCollectionInterface
|
||||
*/
|
||||
private function checkSubdomains($subdomains)
|
||||
{
|
||||
// CLI calls can't be on subdomain.
|
||||
if (! isset($_SERVER['HTTP_HOST']))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_null($this->currentSubdomain))
|
||||
{
|
||||
$this->currentSubdomain = $this->determineCurrentSubdomain();
|
||||
|
@ -469,7 +469,7 @@ class Router implements RouterInterface
|
||||
{
|
||||
$val = preg_replace('#^' . $key . '$#', $val, $uri);
|
||||
}
|
||||
elseif (strpos($key, '/') !== false)
|
||||
elseif (strpos($val, '/') !== false)
|
||||
{
|
||||
$val = str_replace('/', '\\', $val);
|
||||
}
|
||||
@ -561,6 +561,8 @@ class Router implements RouterInterface
|
||||
*/
|
||||
protected function validateRequest(array $segments)
|
||||
{
|
||||
$segments = array_filter($segments);
|
||||
|
||||
$c = count($segments);
|
||||
$directory_override = isset($this->directory);
|
||||
|
||||
@ -571,8 +573,7 @@ class Router implements RouterInterface
|
||||
$test = $this->directory . ucfirst($this->translateURIDashes === true ? str_replace('-', '_', $segments[0]) : $segments[0]
|
||||
);
|
||||
|
||||
if (! is_file(APPPATH . 'Controllers/' . $test . '.php') && $directory_override === false && is_dir(APPPATH . 'Controllers/' . $this->directory . ucfirst($segments[0]))
|
||||
)
|
||||
if (! is_file(APPPATH . 'Controllers/' . $test . '.php') && $directory_override === false && is_dir(APPPATH . 'Controllers/' . $this->directory . ucfirst($segments[0])))
|
||||
{
|
||||
$this->setDirectory(array_shift($segments), true);
|
||||
continue;
|
||||
|
@ -209,5 +209,4 @@ abstract class BaseHandler implements \SessionHandlerInterface
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -73,6 +73,16 @@ class FileHandler extends BaseHandler implements \SessionHandlerInterface
|
||||
*/
|
||||
protected $fileNew;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $matchIP = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sessionIDRegex;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@ -91,14 +101,19 @@ class FileHandler extends BaseHandler implements \SessionHandlerInterface
|
||||
}
|
||||
else
|
||||
{
|
||||
$sessionPath = rtrim(ini_get('session.save_path'), '/\\');
|
||||
$sessionPath = rtrim(ini_get('session.save_path'), '/\\');
|
||||
|
||||
if (! $sessionPath)
|
||||
{
|
||||
{
|
||||
$sessionPath = WRITEPATH . 'session';
|
||||
}
|
||||
|
||||
$this->savePath = $sessionPath;
|
||||
$this->savePath = $sessionPath;
|
||||
}
|
||||
|
||||
$this->matchIP = $config->sessionMatchIP;
|
||||
|
||||
$this->configureSessionIDRegex();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -130,8 +145,8 @@ class FileHandler extends BaseHandler implements \SessionHandlerInterface
|
||||
|
||||
$this->savePath = $savePath;
|
||||
$this->filePath = $this->savePath . '/'
|
||||
. $name // we'll use the session cookie name as a prefix to avoid collisions
|
||||
. ($this->matchIP ? md5($this->ipAddress) : '');
|
||||
. $name // we'll use the session cookie name as a prefix to avoid collisions
|
||||
. ($this->matchIP ? md5($this->ipAddress) : '');
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -219,7 +234,7 @@ class FileHandler extends BaseHandler implements \SessionHandlerInterface
|
||||
{
|
||||
// If the two IDs don't match, we have a session_regenerate_id() call
|
||||
// and we need to close the old handle and open a new one
|
||||
if ($sessionID !== $this->sessionID && ( ! $this->close() || $this->read($sessionID) === false))
|
||||
if ($sessionID !== $this->sessionID && (! $this->close() || $this->read($sessionID) === false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -302,13 +317,15 @@ class FileHandler extends BaseHandler implements \SessionHandlerInterface
|
||||
{
|
||||
if ($this->close())
|
||||
{
|
||||
return is_file($this->filePath . $session_id) ? (unlink($this->filePath . $session_id) && $this->destroyCookie()) : true;
|
||||
return is_file($this->filePath . $session_id)
|
||||
? (unlink($this->filePath . $session_id) && $this->destroyCookie()) : true;
|
||||
}
|
||||
elseif ($this->filePath !== null)
|
||||
{
|
||||
clearstatcache();
|
||||
|
||||
return is_file($this->filePath . $session_id) ? (unlink($this->filePath . $session_id) && $this->destroyCookie()) : true;
|
||||
return is_file($this->filePath . $session_id)
|
||||
? (unlink($this->filePath . $session_id) && $this->destroyCookie()) : true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -336,20 +353,28 @@ class FileHandler extends BaseHandler implements \SessionHandlerInterface
|
||||
|
||||
$ts = time() - $maxlifetime;
|
||||
|
||||
$pattern = $this->matchIP === true
|
||||
? '[0-9a-f]{32}'
|
||||
: '';
|
||||
|
||||
$pattern = sprintf(
|
||||
'/^%s[0-9a-f]{%d}$/', preg_quote($this->cookieName, '/'), ($this->matchIP === true ? 72 : 40)
|
||||
'#\A%s' . $pattern . $this->sessionIDRegex . '\z#',
|
||||
preg_quote($this->cookieName)
|
||||
);
|
||||
|
||||
while (($file = readdir($directory)) !== false)
|
||||
{
|
||||
// If the filename doesn't match this pattern, it's either not a session file or is not ours
|
||||
if (! preg_match($pattern, $file) || ! is_file($this->savePath . '/' . $file) || ($mtime = filemtime($this->savePath . '/' . $file)) === false || $mtime > $ts
|
||||
if (! preg_match($pattern, $file)
|
||||
|| ! is_file($this->savePath . DIRECTORY_SEPARATOR . $file)
|
||||
|| ($mtime = filemtime($this->savePath . DIRECTORY_SEPARATOR . $file)) === false
|
||||
|| $mtime > $ts
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unlink($this->savePath . '/' . $file);
|
||||
unlink($this->savePath . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
|
||||
closedir($directory);
|
||||
@ -358,4 +383,36 @@ class FileHandler extends BaseHandler implements \SessionHandlerInterface
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Configure Session ID regular expression
|
||||
*/
|
||||
protected function configureSessionIDRegex()
|
||||
{
|
||||
$bitsPerCharacter = (int)ini_get('session.sid_bits_per_character');
|
||||
$SIDLength = (int)ini_get('session.sid_length');
|
||||
|
||||
if (($bits = $SIDLength * $bitsPerCharacter) < 160)
|
||||
{
|
||||
// Add as many more characters as necessary to reach at least 160 bits
|
||||
$SIDLength += (int)ceil((160 % $bits) / $bitsPerCharacter);
|
||||
ini_set('session.sid_length', $SIDLength);
|
||||
}
|
||||
|
||||
// Yes, 4,5,6 are the only known possible values as of 2016-10-27
|
||||
switch ($bitsPerCharacter)
|
||||
{
|
||||
case 4:
|
||||
$this->sessionIDRegex = '[0-9a-f]';
|
||||
break;
|
||||
case 5:
|
||||
$this->sessionIDRegex = '[0-9a-v]';
|
||||
break;
|
||||
case 6:
|
||||
$this->sessionIDRegex = '[0-9a-zA-Z,-]';
|
||||
break;
|
||||
}
|
||||
|
||||
$this->sessionIDRegex .= '{' . $SIDLength . '}';
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +94,11 @@ class MemcachedHandler extends BaseHandler implements \SessionHandlerInterface
|
||||
{
|
||||
$this->keyPrefix .= $this->ipAddress . ':';
|
||||
}
|
||||
|
||||
if(!empty($this->keyPrefix))
|
||||
{
|
||||
ini_set('memcached.sess_prefix', $this->keyPrefix);
|
||||
}
|
||||
|
||||
$this->sessionExpiration = $config->sessionExpiration;
|
||||
}
|
||||
@ -184,7 +189,7 @@ class MemcachedHandler extends BaseHandler implements \SessionHandlerInterface
|
||||
return $session_data;
|
||||
}
|
||||
|
||||
return false;
|
||||
return '';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
@ -42,7 +42,7 @@ use Psr\Log\LoggerAwareTrait;
|
||||
* Implementation of CodeIgniter session container.
|
||||
*
|
||||
* Session configuration is done through session variables and cookie related
|
||||
* variables in application/config/App.php
|
||||
* variables in app/config/App.php
|
||||
*/
|
||||
class Session implements SessionInterface
|
||||
{
|
||||
@ -52,7 +52,7 @@ class Session implements SessionInterface
|
||||
/**
|
||||
* Instance of the driver to use.
|
||||
*
|
||||
* @var HandlerInterface
|
||||
* @var \CodeIgniter\Log\Handlers\HandlerInterface
|
||||
*/
|
||||
protected $driver;
|
||||
|
||||
@ -303,6 +303,11 @@ class Session implements SessionInterface
|
||||
{
|
||||
ini_set('session.gc_maxlifetime', (int) $this->sessionExpiration);
|
||||
}
|
||||
|
||||
if(!empty($this->sessionSavePath))
|
||||
{
|
||||
ini_set('session.save_path', $this->sessionSavePath);
|
||||
}
|
||||
|
||||
// Security is king
|
||||
ini_set('session.use_trans_sid', 0);
|
||||
|
@ -1,6 +1,41 @@
|
||||
<?php namespace Tests\Support\Helpers;
|
||||
<?php namespace CodeIgniter\Test;
|
||||
|
||||
/**
|
||||
* 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 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
use Tests\Support\DOM\DOMParser;
|
||||
use CodeIgniter\HTTP\RedirectResponse;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
@ -1,6 +1,43 @@
|
||||
<?php namespace Tests\Support\Helpers;
|
||||
<?php namespace CodeIgniter\Test;
|
||||
|
||||
/**
|
||||
* 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 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
use CodeIgniter\HTTP\IncomingRequest;
|
||||
use CodeIgniter\HTTP\UserAgent;
|
||||
use CodeIgniter\HTTP\Response;
|
||||
use CodeIgniter\HTTP\URI;
|
||||
use Config\App;
|
||||
@ -54,7 +91,7 @@ trait ControllerTester
|
||||
|
||||
if (empty($this->request))
|
||||
{
|
||||
$this->request = new IncomingRequest($this->appConfig, $this->uri, $this->body);
|
||||
$this->request = new IncomingRequest($this->appConfig, $this->uri, $this->body, new UserAgent());
|
||||
}
|
||||
|
||||
if (empty($this->response))
|
||||
@ -73,7 +110,7 @@ trait ControllerTester
|
||||
* @param string $method
|
||||
* @param array $params
|
||||
*
|
||||
* @return \Tests\Support\Helpers\ControllerResponse
|
||||
* @return \CodeIgniter\Test\ControllerResponse|\InvalidArgumentException
|
||||
*/
|
||||
public function execute(string $method, ...$params)
|
||||
{
|
@ -1,4 +1,40 @@
|
||||
<?php namespace Tests\Support\DOM;
|
||||
<?php namespace CodeIgniter\Test;
|
||||
|
||||
/**
|
||||
* 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 2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
* @link https://codeigniter.com
|
||||
* @since Version 3.0.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
class DOMParser
|
||||
{
|
||||
@ -59,7 +95,7 @@ class DOMParser
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return \Tests\Support\DOM\DOMParser
|
||||
* @return \CodeIgniter\Test\DOMParser
|
||||
*/
|
||||
public function withFile(string $path)
|
||||
{
|
@ -3,7 +3,6 @@
|
||||
use CodeIgniter\HTTP\RedirectResponse;
|
||||
use CodeIgniter\HTTP\Response;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Tests\Support\DOM\DOMParser;
|
||||
|
||||
class FeatureResponse extends TestCase
|
||||
{
|
||||
@ -13,7 +12,7 @@ class FeatureResponse extends TestCase
|
||||
public $response;
|
||||
|
||||
/**
|
||||
* @var \Tests\Support\DOM\DOMParser
|
||||
* @var \CodeIgniter\Test\DOMParser
|
||||
*/
|
||||
protected $domParser;
|
||||
|
||||
|
@ -304,9 +304,9 @@ class Rules
|
||||
// If the field is present we can safely assume that
|
||||
// the field is here, no matter whether the corresponding
|
||||
// search field is present or not.
|
||||
$present = $this->required($data[$str] ?? null);
|
||||
$present = $this->required($str ?? '');
|
||||
|
||||
if ($present === true)
|
||||
if ($present)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -356,9 +356,9 @@ class Rules
|
||||
// If the field is present we can safely assume that
|
||||
// the field is here, no matter whether the corresponding
|
||||
// search field is present or not.
|
||||
$present = $this->required($data[$str] ?? null);
|
||||
$present = $this->required($str ?? '');
|
||||
|
||||
if ($present === true)
|
||||
if ($present)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user