diff --git a/.gitignore b/.gitignore index 32c14d01b59b..f30743491a5a 100644 --- a/.gitignore +++ b/.gitignore @@ -126,4 +126,4 @@ nb-configuration.xml .vscode/ /results/ -/phpunit.xml +/phpunit*.xml diff --git a/application/Config/App.php b/application/Config/App.php index 94a2bab44abf..9dc72bf4d467 100644 --- a/application/Config/App.php +++ b/application/Config/App.php @@ -215,7 +215,7 @@ class App extends BaseConfig | Reverse Proxy IPs |-------------------------------------------------------------------------- | - | If your getServer is behind a reverse proxy, you must whitelist the proxy + | If your server is behind a reverse proxy, you must whitelist the proxy | IP addresses from which CodeIgniter should trust headers such as | HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify | the visitor's IP address. @@ -240,11 +240,13 @@ class App extends BaseConfig | CSRFCookieName = The cookie name | CSRFExpire = The number in seconds the token should expire. | CSRFRegenerate = Regenerate token on every submission + | CSRFRedirect = Redirect to previous page with error on failure */ public $CSRFTokenName = 'csrf_test_name'; public $CSRFCookieName = 'csrf_cookie_name'; public $CSRFExpire = 7200; public $CSRFRegenerate = true; + public $CSRFRedirect = true; /* |-------------------------------------------------------------------------- diff --git a/application/Config/DocTypes.php b/application/Config/DocTypes.php index 37830984c31f..38ceaedfc905 100755 --- a/application/Config/DocTypes.php +++ b/application/Config/DocTypes.php @@ -8,7 +8,7 @@ class DocTypes { - static $list = + public $list = [ 'xhtml11' => '', 'xhtml1-strict' => '', diff --git a/application/Controllers/Checks.php b/application/Controllers/Checks.php deleted file mode 100644 index 5fbd34cfb412..000000000000 --- a/application/Controllers/Checks.php +++ /dev/null @@ -1,457 +0,0 @@ -start(); - } - - public function forge() - { - echo '

MySQL

'; - - log_message('debug', 'MYSQL TEST'); - - $forge_mysql = \Config\Database::forge(); - - $forge_mysql->getConnection()->query('SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;'); - - $forge_mysql->dropTable('users', true); - - $forge_mysql->getConnection()->query('SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;'); - - $forge_mysql->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 11 - ], - 'name' => [ - 'type' => 'VARCHAR', - 'constraint' => 50, - ] - ]); - $forge_mysql->addKey('id', true); - $attributes = array('ENGINE' => 'InnoDB'); - $forge_mysql->createTable('users', true, $attributes); - - $data_insert = array( - 'id' => 1, - 'name' => 'User 1', - ); - $forge_mysql->getConnection()->table('users')->insert($data_insert); - - $drop = $forge_mysql->dropTable('invoices', true); - - $forge_mysql->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 11, - ], - 'users_id' => [ - 'type' => 'INTEGER', - 'constraint' => 11 - ], - 'other_id' => [ - 'type' => 'INTEGER', - 'constraint' => 11 - ] - ]); - $forge_mysql->addKey('id', true); - - $forge_mysql->addForeignKey('users_id','users','id','CASCADE','CASCADE'); - $forge_mysql->addForeignKey('other_id','users','id','CASCADE','CASCADE'); - - $attributes = array('ENGINE' => 'InnoDB'); - $res = $forge_mysql->createTable('invoices', true,$attributes); - - if(!$res){ - var_dump($forge_mysql->getConnection()->mysqli); - }else{ - echo '

OK'; - - var_dump($forge_mysql->getConnection()->getForeignKeyData('invoices')); - } - - $res = $forge_mysql->dropForeignKey('invoices','invoices_other_id_foreign'); - - - echo '

PostgreSQL

'; - - $forge_pgsql = \Config\Database::forge('pgsql'); - - $forge_pgsql->dropTable('users',true, true); - - $forge_pgsql->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 11, - 'auto_increment' => true, - ], - 'name' => [ - 'type' => 'VARCHAR', - 'constraint' => 50, - ] - ]); - $forge_pgsql->addKey('id', true); - $forge_pgsql->createTable('users', true); - - - $data_insert = array( - 'id' => 1, - 'name' => 'User 1', - ); - $forge_pgsql->getConnection()->table('users')->insert($data_insert); - - $forge_pgsql->dropTable('invoices',true); - $forge_pgsql->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 11, - 'auto_increment' => true, - ], - 'users_id' => [ - 'type' => 'INTEGER', - 'constraint' => 11 - ], - 'other_id' => [ - 'type' => 'INTEGER', - 'constraint' => 11 - ], - 'another_id' => [ - 'type' => 'INTEGER', - 'constraint' => 11 - ] - ]); - $forge_pgsql->addKey('id', true); - - $forge_pgsql->addForeignKey('users_id','users','id','CASCADE','CASCADE'); - $forge_pgsql->addForeignKey('other_id','users','id'); - - $res = $forge_pgsql->createTable('invoices', true); - - if(!$res){ - var_dump($forge_pgsql->getConnection()->mysqli); - }else{ - echo '

OK'; - var_dump($forge_pgsql->getConnection()->getForeignKeyData('invoices')); - } - - //$res = $forge_pgsql->dropForeignKey('invoices','invoices_other_id_foreign'); - - } - - - public function escape() - { - $db = Database::connect(); - $db->initialize(); - - $jobs = $db->table('job') - ->whereNotIn('name', ['Politician', 'Accountant']) - ->get() - ->getResult(); - - die(var_dump($jobs)); - } - - public function password() - { - $db = Database::connect(); - $db->initialize(); - - $result = $db->table('misc') - ->insert([ - 'key' => 'password', - 'value' => '$2y$10$ErQlCj/Mo10il.FthAm0WOjYdf3chZEGPFqaPzjqOX2aj2uYf5Ihq' - ]); - - die(var_dump($result)); - } - - - public function forms() - { - helper('form'); - - var_dump(form_open()); - } - - public function api() - { - $data = [ - "total_users" => 3, - "users" => [ - [ - "id" => 1, - "name" => "Nitya", - "address" => [ - "country" => "India", - "city" => "Kolkata", - "zip" => 700102, - ] - ], - [ - "id" => 2, - "name" => "John", - "address" => [ - "country" => "USA", - "city" => "Newyork", - "zip" => "NY1234", - ] - ], - [ - "id" => 3, - "name" => "Viktor", - "address" => [ - "country" => "Australia", - "city" => "Sydney", - "zip" => 123456, - ] - ], - ] - ]; - - return $this->respond($data); - } - - public function db() - { - $db = Database::connect(); - $db->initialize(); - - $query = $db->prepare(function($db){ - return $db->table('user')->insert([ - 'name' => 'a', - 'email' => 'b@example.com', - 'country' => 'x' - ]); - }); - - $query->execute('foo', 'foo@example.com', 'US'); - } - - public function db2() - { - $db = Database::connect(); - $db->initialize(); - - $db->table('user')->insert([ - 'name' => 'a', - 'email' => 'b@example.com', - 'country' => 'x' - ]); - } - - public function format() - { - echo '
';
-		var_dump($this->response->getHeaderLine('content-type'));
-	}
-
-	public function model()
-	{
-	    $model = new class() extends Model {
-	        protected $table = 'job';
-        };
-
-	    $results = $model->findAll();
-
-	    $developer = $model->findWhere('name', 'Developer');
-
-	    $politician = $model->find(3);
-
-	    dd($politician);
-
-	}
-
-    public function curl()
-    {
-        $client = Services::curlrequest([
-            'debug' => true,
-            'follow_redirects' => true,
-            'json' => ['foo' => 'bar']
-        ]);
-
-        echo '
';
-        $response = $client->request('PUT', 'http://ci4.dev/checks/catch');
-        echo $response->getBody();
-    }
-
-    // Simply echos back what's given in the body.
-    public function catch()
-    {
-        $body = print_r($this->request->getRawInput(), true);
-        echo $body;
-    }
-
-	public function redirect()
-	{
-		return redirect('/checks/model');
-    }
-
-	public function image()
-	{
-		$info = Services::image('imagick')
-			->withFile("/Users/kilishan/Documents/BobHeader.jpg")
-			->getFile()
-			->getProperties(true);
-
-		dd(ENVIRONMENT);
-
-		$images = Services::image('imagick')
-			->getVersion();
-//			->withFile("/Users/kilishan/Documents/BobHeader.jpg")
-//			->resize(500, 100, true)
-//			->crop(200, 75, 20, 0, false)
-//			->rotate(90)
-//			->save('/Users/kilishan/temp.jpg');
-
-//		$images = Services::image('imagick')
-//			->withFile("/Users/kilishan/Documents/BobHeader.jpg")
-//			->fit(500, 100, 'bottom-left')
-//			->text('Bob is Back!', [
-//				'fontPath'  => '/Users/kilishan/Downloads/Calibri.ttf',
-//				'fontSize' => 40,
-//				'padding' => 0,
-//				'opacity'   => 0.5,
-//				'vAlign'    => 'top',
-//				'hAlign'    => 'right',
-//				'withShadow' => true,
-//			])
-//			->save('/Users/kilishan/temp.jpg', 100);
-
-
-		ddd($images);
-	}
-
-	public function time()
-	{
-		$time = new Time();
-
-		echo($time);
-		echo '
'; - echo Time::now(); - echo '
'; - echo Time::parse('First Monday of December'); - echo '
'; - - $time = new Time('Next Monday'); - die($time); - } - - public function csp() - { -// $this->response->CSP->reportOnly(true); - $this->response->CSP->setDefaultSrc(base_url()); - $this->response->CSP->addStyleSrc('unsafe-inline'); - $this->response->CSP->addStyleSrc('https://maxcdn.bootstrapcdn.com'); - - echo << - - - - - - - - - -EOF; - - } - - public function upload() - { - if ($this->request->getMethod() == 'post') - { - $this->validate([ - 'avatar' => 'uploaded[avatar]|ext_in[avatar,png,jpg,jpeg,gif]' - ]); - - /** - * @var \CodeIgniter\HTTP\Files\UploadedFile - */ - $file = $this->request->getFile('avatar'); - - echo "Name: {$file->getName()}
"; - echo "Temp Name: {$file->getTempName()}
"; - echo "Original Name: {$file->getClientName()}
"; - echo "Random Name: {$file->getRandomName()}
"; - echo "Extension: {$file->getExtension()}
"; - echo "Client Extension: {$file->getClientExtension()}
"; - echo "Guessed Extension: {$file->guessExtension()}
"; - echo "MimeType: {$file->getMimeType()}
"; - echo "IsValid: {$file->isValid()}
"; - echo "Size (b): {$file->getSize()}
"; - echo "Size (kb): {$file->getSize('kb')}
"; - echo "Size (mb): {$file->getSize('mb')}
"; - echo "Size (mb): {$file->getSize('mb')}
"; - echo "Path: {$file->getPath()}
"; - echo "RealPath: {$file->getRealPath()}
"; - echo "Filename: {$file->getFilename()}
"; - echo "Basename: {$file->getBasename()}
"; - echo "Pathname: {$file->getPathname()}
"; - echo "Permissions: {$file->getPerms()}
"; - echo "Inode: {$file->getInode()}
"; - echo "Owner: {$file->getOwner()}
"; - echo "Group: {$file->getGroup()}
"; - echo "ATime: {$file->getATime()}
"; - echo "MTime: {$file->getMTime()}
"; - echo "CTime: {$file->getCTime()}
"; - - dd($file); - } - - echo << - - - -
- - - - - -
- - - - -EOF; -; - } - - public function parser() - { - $this->parser = Services::parser(); - } - - public function error() - { - throw new \RuntimeException('Oops!', 403); - } - -} diff --git a/application/Filters/CSRF.php b/application/Filters/CSRF.php index 46f1582300a8..221ae1316497 100644 --- a/application/Filters/CSRF.php +++ b/application/Filters/CSRF.php @@ -3,6 +3,7 @@ use CodeIgniter\Filters\FilterInterface; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; +use CodeIgniter\Security\Exceptions\SecurityException; use Config\Services; class CSRF implements FilterInterface @@ -30,7 +31,19 @@ public function before(RequestInterface $request) $security = Services::security(); - $security->CSRFVerify($request); + try + { + $security->CSRFVerify($request); + } + catch (SecurityException $e) + { + if (config('App')->CSRFRedirect && ! $request->isAJAX()) + { + return redirect()->back()->with('error', $e->getMessage()); + } + + throw $e; + } } //-------------------------------------------------------------------- diff --git a/application/Filters/DebugToolbar.php b/application/Filters/DebugToolbar.php index 945108013c47..878a52387923 100644 --- a/application/Filters/DebugToolbar.php +++ b/application/Filters/DebugToolbar.php @@ -37,12 +37,11 @@ public function after(RequestInterface $request, ResponseInterface $response) { global $app; - $toolbar = Services::toolbar(new App()); + $toolbar = Services::toolbar(config(App::class)); $stats = $app->getPerformanceStats(); $data = $toolbar->run( $stats['startTime'], $stats['totalTime'], - $stats['startMemory'], $request, $response ); diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index 0178acff59b6..d27ccf1e03e6 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -50,14 +50,14 @@ * Some of the code in this class is Windows-specific, and not * possible to test using travis-ci. It has been phpunit-annotated * to prevent messing up code coverage. - * + * * Some of the methods require keyboard input, and are not unit-testable * as a result: input() and prompt(). * validate() is internal, and not testable if prompt() isn't. * The wait() method is mostly testable, as long as you don't give it - * an argument of "0". + * an argument of "0". * These have been flagged to ignore for code coverage purposes. - * + * * @package CodeIgniter\HTTP */ class CLI @@ -165,7 +165,7 @@ public static function init() * * @param string $prefix * @return string - * + * * @codeCoverageIgnore */ public static function input(string $prefix = null): string @@ -217,7 +217,7 @@ public static function prompt($field, $options = null, $validation = null): stri $default = $options; } - if (is_array($options) && count($options)) + if (is_array($options) && $options) { $opts = $options; $extra_output_default = static::color($opts[0], 'white'); @@ -379,7 +379,7 @@ public static function wait(int $seconds, bool $countdown = false) */ public static function isWindows() { - return 'win' === strtolower(substr(php_uname("s"), 0, 3)); + return stripos(PHP_OS, 'WIN') === 0; } //-------------------------------------------------------------------- @@ -652,7 +652,7 @@ protected static function parseCommandLine() $value = null; // if there is a following segment, and it doesn't start with a dash, it's a value. - if (isset($_SERVER['argv'][$i + 1]) && mb_substr($_SERVER['argv'][$i + 1], 0, 1) != '-') + if (isset($_SERVER['argv'][$i + 1]) && mb_strpos($_SERVER['argv'][$i + 1], '-') !== 0) { $value = $_SERVER['argv'][$i + 1]; $i ++; diff --git a/system/CLI/CommandRunner.php b/system/CLI/CommandRunner.php index e0b08c558f26..46c2e18f68ff 100644 --- a/system/CLI/CommandRunner.php +++ b/system/CLI/CommandRunner.php @@ -39,7 +39,6 @@ class CommandRunner extends Controller { - /** * Stores the info about found Commands. * @@ -47,6 +46,11 @@ class CommandRunner extends Controller */ protected $commands = []; + /** + * @var \CodeIgniter\Log\Logger + */ + protected $logger; + //-------------------------------------------------------------------- /** diff --git a/system/CLI/Console.php b/system/CLI/Console.php index 3a73e05dc911..7daccf9b0137 100644 --- a/system/CLI/Console.php +++ b/system/CLI/Console.php @@ -63,15 +63,20 @@ public function __construct(CodeIgniter $app) /** * Runs the current command discovered on the CLI. + * + * @param bool $useSafeOutput + * + * @return \CodeIgniter\HTTP\RequestInterface|\CodeIgniter\HTTP\Response|\CodeIgniter\HTTP\ResponseInterface|mixed + * @throws \CodeIgniter\HTTP\RedirectException */ - public function run() + public function run(bool $useSafeOutput = false) { $path = CLI::getURI() ?: 'list'; // Set the path for the application to route to. $this->app->setPath("ci{$path}"); - return $this->app->run(); + return $this->app->useSafeOutput($useSafeOutput)->run(); } //-------------------------------------------------------------------- diff --git a/system/Cache/Handlers/MemcachedHandler.php b/system/Cache/Handlers/MemcachedHandler.php index cd2405489fb1..36513248f6fe 100644 --- a/system/Cache/Handlers/MemcachedHandler.php +++ b/system/Cache/Handlers/MemcachedHandler.php @@ -287,7 +287,8 @@ public function getMetaData(string $key) $stored = $this->memcached->get($key); - if (count($stored) !== 3) + // if not an array, don't try to count for PHP7.2 + if (! is_array($stored) || count($stored) !== 3) { return FALSE; } diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 412fa7447d7a..5b7524635791 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -37,6 +37,7 @@ */ use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\Request; +use CodeIgniter\HTTP\ResponseInterface; use Config\Services; use Config\Cache; use CodeIgniter\HTTP\URI; @@ -66,12 +67,6 @@ class CodeIgniter */ protected $startTime; - /** - * Amount of memory at app start. - * @var int - */ - protected $startMemory; - /** * Total app execution time * @var float @@ -138,12 +133,18 @@ class CodeIgniter */ protected $path; + /** + * Should the Response instance "pretend" + * to keep from setting headers/cookies/etc + * @var bool + */ + protected $useSafeOutput = false; + //-------------------------------------------------------------------- public function __construct($config) { $this->startTime = microtime(true); - $this->startMemory = memory_get_usage(true); $this->config = $config; } @@ -202,12 +203,23 @@ public function run(RouteCollectionInterface $routes = null, bool $returnRespons // Check for a cached page. Execution will stop // if the page has been cached. $cacheConfig = new Cache(); - $this->displayCache($cacheConfig); + $response = $this->displayCache($cacheConfig); + if ($response instanceof ResponseInterface) + { + if ($returnResponse) + { + return $response; + } + + $this->response->pretend($this->useSafeOutput)->send(); + $this->callExit(EXIT_SUCCESS); + } try { return $this->handleRequest($routes, $cacheConfig, $returnResponse); - } catch (Router\RedirectException $e) + } + catch (Router\RedirectException $e) { $logger = Services::logger(); $logger->info('REDIRECTED ROUTE at ' . $e->getMessage()); @@ -225,6 +237,24 @@ public function run(RouteCollectionInterface $routes = null, bool $returnRespons //-------------------------------------------------------------------- + /** + * Set our Response instance to "pretend" mode so that things like + * cookies and headers are not actually sent, allowing PHP 7.2+ to + * not complain when ini_set() function is used. + * + * @param bool $safe + * + * @return $this + */ + public function useSafeOutput(bool $safe = true) + { + $this->useSafeOutput = $safe; + + return $this; + } + + //-------------------------------------------------------------------- + /** * Handles the main request logic and fires the controller. * @@ -232,6 +262,7 @@ public function run(RouteCollectionInterface $routes = null, bool $returnRespons * @param $cacheConfig * @param bool $returnResponse * + * @return \CodeIgniter\HTTP\RequestInterface|\CodeIgniter\HTTP\Response|\CodeIgniter\HTTP\ResponseInterface|mixed * @throws \CodeIgniter\Filters\Exceptions\FilterException */ protected function handleRequest(RouteCollectionInterface $routes = null, $cacheConfig, bool $returnResponse = false) @@ -242,7 +273,11 @@ protected function handleRequest(RouteCollectionInterface $routes = null, $cache $filters = Services::filters(); $uri = $this->request instanceof CLIRequest ? $this->request->getPath() : $this->request->uri->getPath(); - $filters->run($uri, 'before'); + $possibleRedirect = $filters->run($uri, 'before'); + if($possibleRedirect instanceof RedirectResponse) + { + return $possibleRedirect; + } $returned = $this->startController(); @@ -495,8 +530,9 @@ public function displayCache($config) } $output = $this->displayPerformanceMetrics($output); - $this->response->setBody($output)->send(); - $this->callExit(EXIT_SUCCESS); + $this->response->setBody($output); + + return $this->response; }; } @@ -549,7 +585,6 @@ public function getPerformanceStats() return [ 'startTime' => $this->startTime, 'totalTime' => $this->totalTime, - 'startMemory' => $this->startMemory ]; } @@ -727,7 +762,8 @@ protected function startController() */ protected function createController() { - $class = new $this->controller($this->request, $this->response); + $class = new $this->controller(); + $class->initController($this->request, $this->response, Services::logger()); $this->benchmark->stop('controller_constructor'); @@ -917,7 +953,7 @@ public function spoofRequestMethod() */ protected function sendResponse() { - $this->response->send(); + $this->response->pretend($this->useSafeOutput)->send(); } //-------------------------------------------------------------------- diff --git a/system/Commands/Database/MigrateStatus.php b/system/Commands/Database/MigrateStatus.php index 364dc422c90e..b51dfb7a3a80 100644 --- a/system/Commands/Database/MigrateStatus.php +++ b/system/Commands/Database/MigrateStatus.php @@ -93,6 +93,12 @@ class MigrateStatus extends BaseCommand '-g' => 'Set database group', ]; + protected $ignoredNamespaces = [ + 'CodeIgniter', + 'Config', + 'Tests\Support' + ]; + /** * Displays a list of all migrations and whether they've been run or not. * @@ -114,25 +120,25 @@ public function run(array $params = []) // Loop for all $namespaces foreach ($namespaces as $namespace => $path) { + if (in_array($namespace, $this->ignoredNamespaces)) + { + continue; + } $runner->setNamespace($namespace); $migrations = $runner->findMigrations(); $history = $runner->getHistory(); + CLI::write($namespace); + if (empty($migrations)) { - CLI::error("$namespace: " . lang('Migrations.noneFound')); + CLI::error(lang('Migrations.noneFound')); continue; } ksort($migrations); - CLI::newLine(1); - - CLI::write(lang('Migrations.historyFor') . "$namespace: ", 'green'); - - CLI::newLine(1); - $max = 0; foreach ($migrations as $version => $migration) { @@ -142,7 +148,7 @@ public function run(array $params = []) $max = max($max, strlen($file)); } - CLI::write(str_pad(lang('Migrations.filename'), $max + 6) . lang('Migrations.on'), 'yellow'); + CLI::write(' '. str_pad(lang('Migrations.filename'), $max + 4) . lang('Migrations.on'), 'yellow'); foreach ($migrations as $version => $migration) @@ -157,7 +163,7 @@ public function run(array $params = []) $date = date("Y-m-d H:i:s", $row['time']); } - CLI::write(str_pad($migration->name, $max + 6) . ($date ? $date : '---')); + CLI::write(str_pad(' '.$migration->name, $max + 6) . ($date ? $date : '---')); } } } diff --git a/system/Commands/ListCommands.php b/system/Commands/ListCommands.php index 121c5a26c8f3..d17c349cfe00 100644 --- a/system/Commands/ListCommands.php +++ b/system/Commands/ListCommands.php @@ -125,77 +125,68 @@ public function run(array $params) */ protected function describeCommands(array $commands = []) { - arsort($commands); + ksort($commands); - $names = array_keys($commands); - $descs = array_column($commands, 'description'); - $groups = array_column($commands, 'group'); - $lastGroup = ''; + // Sort into buckets by group + $sorted = []; + $maxTitleLength = 0; - // Pad each item to the same length - $names = $this->padArray($names, 2, 2); - $countNames = count($names); - for ($i = 0; $i < $countNames; $i ++ ) + foreach ($commands as $title => $command) { - $lastGroup = $this->describeGroup($groups[$i], $lastGroup); - - $out = CLI::color($names[$i], 'yellow'); - - if (isset($descs[$i])) + if (! isset($sorted[$command['group']])) { - $out .= CLI::wrap($descs[$i], 125, strlen($names[$i])); + $sorted[$command['group']] = []; } - CLI::write($out); + $sorted[$command['group']][$title] = $command; + + $maxTitleLength = max($maxTitleLength, strlen($title)); } - } - //-------------------------------------------------------------------- + ksort($sorted); - /** - * Outputs the description, if necessary. - * - * @param string $new - * @param string $old - * - * @return string - */ - protected function describeGroup(string $new, string $old) - { - if ($new == $old) + // Display it all... + foreach ($sorted as $group => $items) { - return $old; - } + CLI::newLine(); + CLI::write($group); - CLI::newLine(); - CLI::write($new); + foreach ($items as $title => $item) + { + $title = $this->padTitle($title, $maxTitleLength, 2, 2); + + $out = CLI::color($title, 'yellow'); + + if (isset($item['description'])) + { + $out .= CLI::wrap($item['description'], 125, strlen($title)); + } - return $new; + CLI::write($out); + } + } } //-------------------------------------------------------------------- /** - * Returns a new array where all of the string elements have - * been padding with trailing spaces to be the same length. + * Pads our string out so that all titles are the same length to nicely line up descriptions. * - * @param array $array - * @param int $extra // How many extra spaces to add at the end - * @param int $indent + * @param string $item + * @param $max + * @param int $extra // How many extra spaces to add at the end + * @param int $indent * * @return array */ - protected function padArray($array, $extra = 2, $indent = 0) + protected function padTitle(string $item, $max, $extra = 2, $indent = 0) { - $max = max(array_map('strlen', $array)) + $extra + $indent; + $max += $extra + $indent; - foreach ($array as &$item) - { - $item = str_repeat(' ', $indent) . $item; - $item = str_pad($item, $max); - } + $item = str_repeat(' ', $indent) . $item; + $item = str_pad($item, $max); - return $array; + return $item; } //-------------------------------------------------------------------- diff --git a/system/Commands/Server/rewrite.php b/system/Commands/Server/rewrite.php index a7af4d421149..aedc5deef350 100644 --- a/system/Commands/Server/rewrite.php +++ b/system/Commands/Server/rewrite.php @@ -6,7 +6,7 @@ * development server based around PHP's built-in development * server. This file simply tries to mimic Apache's mod_rewrite * functionality so the site will operate as normal. - * + * */ // @codeCoverageIgnoreStart // Avoid this file run when listing commands @@ -22,7 +22,7 @@ $uri = urldecode(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)); // Front Controller path - expected to be in the default folder -$fcpath = realpath(__DIR__ . '/../../../public') . DIRECTORY_SEPARATOR; +$fcpath = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR; // Full path $path = $fcpath . ltrim($uri, '/'); diff --git a/system/Common.php b/system/Common.php index 72cbe76ed969..62bd6032826d 100644 --- a/system/Common.php +++ b/system/Common.php @@ -270,8 +270,16 @@ function esc($data, $context = 'html', $encoding = null) $method = 'escape' . ucfirst($context); } - // @todo Optimize this to only load a single instance during page request. - $escaper = new \Zend\Escaper\Escaper($encoding); + static $escaper; + if (! $escaper) + { + $escaper = new \Zend\Escaper\Escaper($encoding); + } + + if ($encoding && $escaper->getEncoding() !== $encoding) + { + $escaper = new \Zend\Escaper\Escaper($encoding); + } $data = $escaper->$method($data); } @@ -605,7 +613,7 @@ function helper($filenames) */ function app_timezone() { - $config = new \Config\App(); + $config = config(\Config\App::class); return $config->appTimezone; } @@ -626,7 +634,7 @@ function app_timezone() */ function csrf_token() { - $config = new \Config\App(); + $config = config(\Config\App::class); return $config->CSRFTokenName; } @@ -762,7 +770,7 @@ function old(string $key, $default = null, $escape = 'html') } // If the result was serialized array or string, then unserialize it for use... - if (substr($value, 0, 2) == 'a:' || substr($value, 0, 2) == 's:') + if (strpos($value, 'a:') === 0 || strpos($value, 's:') === 0) { $value = unserialize($value); } @@ -919,7 +927,7 @@ function is_really_writable($file) */ function slash_item($item) { - $config = new \Config\App(); + $config = config(\Config\App::class); $configItem = $config->{$item}; if ( ! isset($configItem) || empty(trim($configItem))) diff --git a/system/Config/BaseService.php b/system/Config/BaseService.php index b8ffa3b27c07..6fda4d33e6ab 100644 --- a/system/Config/BaseService.php +++ b/system/Config/BaseService.php @@ -202,7 +202,7 @@ protected static function discoverServices(string $name, array $arguments) } } - if (! count(static::$services)) + if (! static::$services) { return; } diff --git a/system/Config/Config.php b/system/Config/Config.php index 0cf79dd3e744..8cef1942d5be 100644 --- a/system/Config/Config.php +++ b/system/Config/Config.php @@ -70,8 +70,6 @@ public static function get(string $name, bool $getShared = true) $class = substr($name, $pos + 1); } - $class = strtolower($class); - if (! $getShared) { return self::createClass($name); diff --git a/system/Config/DotEnv.php b/system/Config/DotEnv.php index c3684317e5c9..0ac1f24c9c29 100644 --- a/system/Config/DotEnv.php +++ b/system/Config/DotEnv.php @@ -109,6 +109,7 @@ public function load() } } + return true; // for success } //-------------------------------------------------------------------- diff --git a/system/Config/Services.php b/system/Config/Services.php index 90f914c44974..68edf67bbedc 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -35,11 +35,10 @@ * @since Version 3.0.0 * @filesource */ +use Config\App; use CodeIgniter\Database\ConnectionInterface; use CodeIgniter\Database\MigrationRunner; -use CodeIgniter\HTTP\URI; use CodeIgniter\View\RendererInterface; -use Config\App; /** * Services Configuration file. @@ -124,7 +123,7 @@ public static function clirequest(\Config\App $config = null, $getShared = true) if (! is_object($config)) { - $config = new \Config\App(); + $config = config(App::class); } return new \CodeIgniter\HTTP\CLIRequest($config); @@ -143,12 +142,7 @@ public static function clirequest(\Config\App $config = null, $getShared = true) * * @return \CodeIgniter\HTTP\CURLRequest */ - public static function curlrequest( - array $options = [], - $response = null, - \Config\App $config = null, - $getShared = true - ) { + public static function curlrequest(array $options = [], $response = null, \Config\App $config = null, $getShared = true) { if ($getShared === true) { return self::getSharedInstance('curlrequest', $options, $response, $config); @@ -156,7 +150,7 @@ public static function curlrequest( if (! is_object($config)) { - $config = new \Config\App(); + $config = config(App::class); } if (! is_object($response)) @@ -540,7 +534,7 @@ public static function request(\Config\App $config = null, $getShared = true) if (! is_object($config)) { - $config = new App(); + $config = config(App::class); } return new \CodeIgniter\HTTP\IncomingRequest( @@ -570,7 +564,7 @@ public static function response(\Config\App $config = null, $getShared = true) if (! is_object($config)) { - $config = new \Config\App(); + $config = config(App::class); } return new \CodeIgniter\HTTP\Response($config); @@ -595,7 +589,7 @@ public static function redirectResponse(\Config\App $config = null, $getShared = if (! is_object($config)) { - $config = new \Config\App(); + $config = config(App::class); } $response = new \CodeIgniter\HTTP\RedirectResponse($config); @@ -671,7 +665,7 @@ public static function security(\Config\App $config = null, $getShared = true) if (! is_object($config)) { - $config = new \Config\App(); + $config = config(App::class); } return new \CodeIgniter\Security\Security($config); @@ -694,7 +688,7 @@ public static function session(\Config\App $config = null, $getShared = true) if (! is_object($config)) { - $config = new \Config\App(); + $config = config(App::class); } $logger = self::logger(true); @@ -771,7 +765,7 @@ public static function toolbar(\Config\App $config = null, $getShared = true) if (! is_object($config)) { - $config = new \Config\App(); + $config = config(App::class); } return new \CodeIgniter\Debug\Toolbar($config); diff --git a/system/Controller.php b/system/Controller.php index e471754e7105..ef1ba0448b33 100644 --- a/system/Controller.php +++ b/system/Controller.php @@ -39,6 +39,7 @@ use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\Log\Logger; use CodeIgniter\Validation\Validation; +use Psr\Log\LoggerInterface; /** * Class Controller @@ -99,18 +100,17 @@ class Controller /** * Constructor. * - * @param RequestInterface $request - * @param ResponseInterface $response - * @param Logger $logger + * @param RequestInterface $request + * @param ResponseInterface $response + * @param \Psr\Log\LoggerInterface $logger + * + * @throws \CodeIgniter\HTTP\RedirectException */ - public function __construct(RequestInterface $request, ResponseInterface $response, Logger $logger = null) + public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger) { $this->request = $request; - $this->response = $response; - - $this->logger = is_null($logger) ? Services::logger(true) : $logger; - + $this->logger = $logger; $this->logger->info('Controller "' . get_class($this) . '" loaded.'); if ($this->forceHTTPS > 0) @@ -132,6 +132,8 @@ public function __construct(RequestInterface $request, ResponseInterface $respon * @param int $duration The number of seconds this link should be * considered secure for. Only with HSTS header. * Default value is 1 year. + * + * @throws \CodeIgniter\HTTP\RedirectException */ public function forceHTTPS(int $duration = 31536000) { @@ -182,9 +184,10 @@ public function validate($rules, array $messages = []): bool { $this->validator = Services::validation(); - $success = $this->validator->withRequest($this->request) - ->setRules($rules, $messages) - ->run(); + $success = $this->validator + ->withRequest($this->request) + ->setRules($rules, $messages) + ->run(); return $success; } diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 9de59e8fd0c9..4bc38037b677 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -1305,6 +1305,28 @@ public function set($key, $value = '', $escape = null) //-------------------------------------------------------------------- + /** + * Returns the previously set() data, alternatively resetting it + * if needed. + * + * @param bool $clean + * + * @return array + */ + public function getSetData(bool $clean = false) + { + $data = $this->QBSet; + + if ($clean) + { + $this->QBSet = []; + } + + return $data; + } + + //-------------------------------------------------------------------- + /** * Get SELECT query string * @@ -1486,13 +1508,13 @@ public function getWhere($where = null, $limit = null, $offset = null) * @param array $set An associative array of insert values * @param bool $escape Whether to escape values and identifiers * - * @param int $batch_size + * @param int $batchSize * @param bool $testing * * @return int Number of rows inserted or FALSE on failure * @throws DatabaseException */ - public function insertBatch($set = null, $escape = null, $batch_size = 100, $testing = false) + public function insertBatch($set = null, $escape = null, $batchSize = 100, $testing = false) { if ($set === null) { @@ -1525,9 +1547,9 @@ public function insertBatch($set = null, $escape = null, $batch_size = 100, $tes // Batch this baby $affected_rows = 0; - for ($i = 0, $total = count($this->QBSet); $i < $total; $i += $batch_size) + for ($i = 0, $total = count($this->QBSet); $i < $total; $i += $batchSize) { - $sql = $this->_insertBatch($this->db->protectIdentifiers($table, true, $escape, false), $this->QBKeys, array_slice($this->QBSet, $i, $batch_size)); + $sql = $this->_insertBatch($this->db->protectIdentifiers($table, true, $escape, false), $this->QBKeys, array_slice($this->QBSet, $i, $batchSize)); if ($testing) { @@ -1953,15 +1975,15 @@ protected function validateUpdate() * * Compiles an update string and runs the query * - * @param array $set An associative array of update values - * @param string $index The where key - * @param int $batch_size The size of the batch to run - * @param bool $returnSQL True means SQL is returned, false will execute the query + * @param array $set An associative array of update values + * @param string $index The where key + * @param int $batchSize The size of the batch to run + * @param bool $returnSQL True means SQL is returned, false will execute the query * * @return mixed Number of rows affected or FALSE on failure * @throws \CodeIgniter\Database\Exceptions\DatabaseException */ - public function updateBatch($set = null, $index = null, $batch_size = 100, $returnSQL = false) + public function updateBatch($set = null, $index = null, $batchSize = 100, $returnSQL = false) { if ($index === null) { @@ -2003,9 +2025,9 @@ public function updateBatch($set = null, $index = null, $batch_size = 100, $retu // Batch this baby $affected_rows = 0; $savedSQL = []; - for ($i = 0, $total = count($this->QBSet); $i < $total; $i += $batch_size) + for ($i = 0, $total = count($this->QBSet); $i < $total; $i += $batchSize) { - $sql = $this->_updateBatch($table, array_slice($this->QBSet, $i, $batch_size), $this->db->protectIdentifiers($index) + $sql = $this->_updateBatch($table, array_slice($this->QBSet, $i, $batchSize), $this->db->protectIdentifiers($index) ); if ($returnSQL) diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index b0dfab8a4c2a..555d06b76a10 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -1495,7 +1495,7 @@ public function callFunction(string $functionName, ...$params) public function listTables($constrain_by_prefix = FALSE) { // Is there a cached result? - if (isset($this->dataCache['table_names']) && count($this->dataCache['table_names'])) + if (isset($this->dataCache['table_names']) && $this->dataCache['table_names']) { return $this->dataCache['table_names']; } diff --git a/system/Database/Forge.php b/system/Database/Forge.php index 33122ec1dd6c..2b94a1c5d907 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -564,7 +564,7 @@ protected function _createTableAttributes($attributes) { if (is_string($key)) { - $sql .= ' ' . strtoupper($key) . ' ' . $attributes[$key]; + $sql .= ' ' . strtoupper($key) . ' ' . $this->db->escape($attributes[$key]); } } diff --git a/system/Database/MySQLi/Forge.php b/system/Database/MySQLi/Forge.php index b83bd42c3f61..c24ca2e64572 100644 --- a/system/Database/MySQLi/Forge.php +++ b/system/Database/MySQLi/Forge.php @@ -109,18 +109,18 @@ protected function _createTableAttributes($attributes) { if (is_string($key)) { - $sql .= ' ' . strtoupper($key) . ' = ' . $attributes[$key]; + $sql .= ' ' . strtoupper($key) . ' = ' . $this->db->escape($attributes[$key]); } } if ( ! empty($this->db->charset) && ! strpos($sql, 'CHARACTER SET') && ! strpos($sql, 'CHARSET')) { - $sql .= ' DEFAULT CHARACTER SET = ' . $this->db->charset; + $sql .= ' DEFAULT CHARACTER SET = ' . $this->db->escape($this->db->charset); } if ( ! empty($this->db->DBCollat) && ! strpos($sql, 'COLLATE')) { - $sql .= ' COLLATE = ' . $this->db->DBCollat; + $sql .= ' COLLATE = ' . $this->db->escape($this->db->DBCollat); } return $sql; diff --git a/system/Database/Postgre/Builder.php b/system/Database/Postgre/Builder.php index 8c347b8c1346..55e777ded06b 100644 --- a/system/Database/Postgre/Builder.php +++ b/system/Database/Postgre/Builder.php @@ -148,7 +148,7 @@ public function replace($set = null, $returnSQL = false) $this->set($set); } - if (count($this->QBSet) === 0) + if (! $this->QBSet) { if (CI_DEBUG) { diff --git a/system/Database/Postgre/Forge.php b/system/Database/Postgre/Forge.php index cddb9b77bbb4..446aa65f48a7 100644 --- a/system/Database/Postgre/Forge.php +++ b/system/Database/Postgre/Forge.php @@ -76,6 +76,19 @@ class Forge extends \CodeIgniter\Database\Forge //-------------------------------------------------------------------- + /** + * CREATE TABLE attributes + * + * @param array $attributes Associative array of table attributes + * @return string + */ + protected function _createTableAttributes($attributes) + { + return ''; + } + + //-------------------------------------------------------------------- + /** * ALTER TABLE * diff --git a/system/Debug/Toolbar.php b/system/Debug/Toolbar.php index 9399626d633d..ce96b098277d 100644 --- a/system/Debug/Toolbar.php +++ b/system/Debug/Toolbar.php @@ -35,8 +35,9 @@ * @since Version 3.0.0 * @filesource */ -use CodeIgniter\Config\BaseConfig; +use Config\App; use Config\Services; +use CodeIgniter\Config\BaseConfig; use CodeIgniter\Format\XMLFormatter; /** @@ -93,13 +94,12 @@ public function __construct(BaseConfig $config) * * @param float $startTime App start time * @param float $totalTime - * @param float $startMemory * @param \CodeIgniter\HTTP\RequestInterface $request * @param \CodeIgniter\HTTP\ResponseInterface $response * * @return string JSON encoded data */ - public function run($startTime, $totalTime, $startMemory, $request, $response): string + public function run($startTime, $totalTime, $request, $response): string { // Data items used within the view. $data['url'] = current_url(); @@ -107,7 +107,7 @@ public function run($startTime, $totalTime, $startMemory, $request, $response): $data['isAJAX'] = $request->isAJAX(); $data['startTime'] = $startTime; $data['totalTime'] = $totalTime*1000; - $data['totalMemory'] = number_format((memory_get_peak_usage()-$startMemory)/1048576, 3); + $data['totalMemory'] = number_format((memory_get_peak_usage())/1024/1024, 3); $data['segmentDuration'] = $this->roundTo($data['totalTime']/7, 5); $data['segmentCount'] = (int)ceil($data['totalTime']/$data['segmentDuration']); $data['CI_VERSION'] = \CodeIgniter\CodeIgniter::CI_VERSION; @@ -221,7 +221,7 @@ protected static function format(string $data, string $format = 'html') $files = []; $current = self::$request->getGet('debugbar_time'); - $app = new \Config\App; + $app = config(App::class); for ($i = 0; $i < $total; $i++) { diff --git a/system/Debug/Toolbar/Collectors/Config.php b/system/Debug/Toolbar/Collectors/Config.php index 9e290100d374..cd23cc747016 100644 --- a/system/Debug/Toolbar/Collectors/Config.php +++ b/system/Debug/Toolbar/Collectors/Config.php @@ -1,14 +1,14 @@ CodeIgniter::CI_VERSION, diff --git a/system/Debug/Toolbar/Collectors/Database.php b/system/Debug/Toolbar/Collectors/Database.php index 21a81962eca5..84097fae519f 100644 --- a/system/Debug/Toolbar/Collectors/Database.php +++ b/system/Debug/Toolbar/Collectors/Database.php @@ -204,8 +204,8 @@ public function getBadgeValue() */ public function getTitleDetails(): string { - return '(' . count(static::$queries) . ' Queries across ' . count($this->connections) . ' Connection' . - (count($this->connections) > 1 ? 's' : '') . ')'; + return '(' . count(static::$queries) . ' Queries across ' . ($countConnection = count($this->connections)) . ' Connection' . + ($countConnection > 1 ? 's' : '') . ')'; } //-------------------------------------------------------------------- diff --git a/system/Email/Email.php b/system/Email/Email.php index 4a8750ee84b9..018b522ad65a 100644 --- a/system/Email/Email.php +++ b/system/Email/Email.php @@ -1224,7 +1224,7 @@ public function wordWrap($str, $charlim = null) } // Put our markers back - if (count($unwrap) > 0) + if ($unwrap) { foreach ($unwrap as $key => $val) { diff --git a/system/Entity.php b/system/Entity.php index 709021159321..d34bfe3ac96a 100644 --- a/system/Entity.php +++ b/system/Entity.php @@ -348,7 +348,7 @@ protected function castAs($value, string $type) $value = (object)$value; break; case 'array': - if (is_string($value) && (substr($value, 0, 2) === 'a:' || substr($value, 0, 2) === 's:')) + if (is_string($value) && (strpos($value,'a:') === 0 || strpos($value, 's:') === 0)) { $value = unserialize($value); } diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index e5571fafa51b..939907d931e1 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -322,7 +322,7 @@ protected function processMethods() protected function processFilters(string $uri = null) { - if ( ! isset($this->config->filters) || ! count($this->config->filters)) + if ( ! isset($this->config->filters) || ! $this->config->filters) { return; } diff --git a/system/HTTP/CLIRequest.php b/system/HTTP/CLIRequest.php index 746ebeaca0ac..34cc40fed060 100644 --- a/system/HTTP/CLIRequest.php +++ b/system/HTTP/CLIRequest.php @@ -213,7 +213,7 @@ protected function parseCommand() $options_found = true; - if (substr($argv[$i], 0, 1) != '-') + if (strpos($argv[$i], '-') !== 0) { continue; } @@ -222,7 +222,7 @@ protected function parseCommand() $value = null; // If the next item starts with a dash it's a value - if (isset($argv[$i + 1]) && substr($argv[$i + 1], 0, 1) != '-') + if (isset($argv[$i + 1]) && strpos($argv[$i + 1], '-') !== 0) { $value = filter_var($argv[$i + 1], FILTER_SANITIZE_STRING); $i ++; diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php index 8569d79d4739..3eb725c69dfe 100644 --- a/system/HTTP/CURLRequest.php +++ b/system/HTTP/CURLRequest.php @@ -516,7 +516,7 @@ protected function setResponseHeaders(array $headers = []) $this->response->setHeader($title, $value); } - else if (substr($header, 0, 4) == 'HTTP') + else if (strpos($header, 'HTTP') === 0) { preg_match('#^HTTP\/([12]\.[01]) ([0-9]+) (.+)#', $header, $matches); diff --git a/system/HTTP/Exceptions/HTTPException.php b/system/HTTP/Exceptions/HTTPException.php index a8f2ae464255..9b9cf56730aa 100644 --- a/system/HTTP/Exceptions/HTTPException.php +++ b/system/HTTP/Exceptions/HTTPException.php @@ -1,10 +1,12 @@ -getValueDotNotationSyntax($index, $value[$current_index]); } diff --git a/system/HTTP/Files/UploadedFile.php b/system/HTTP/Files/UploadedFile.php index 7fd3e7e9ed22..ef46d3999926 100644 --- a/system/HTTP/Files/UploadedFile.php +++ b/system/HTTP/Files/UploadedFile.php @@ -1,5 +1,7 @@ -path = $path; - $this->name = $originalName; - $this->originalName = $originalName; - $this->originalMimeType = $mimeType; - $this->size = $size; - $this->error = $error; + $this->path = $path; + $this->name = $originalName; + $this->originalName = $originalName; + $this->originalMimeType = $mimeType; + $this->size = $size; + $this->error = $error; parent::__construct($path, false); } @@ -157,30 +159,34 @@ public function move(string $targetPath, string $name = null, bool $overwrite = if ($this->hasMoved) { - throw new FileException('The file has already been moved.'); + throw HTTPException::forAlreadyMoved(); } if ( ! $this->isValid()) { - throw new FileException('The original file is not a valid file.'); + throw HTTPException::forInvalidFile(); } - $targetPath = rtrim($targetPath, '/') . '/'; - $name = is_null($name) ? $this->getName() : $name; + $targetPath = rtrim($targetPath, '/') . '/'; + $name = is_null($name) ? $this->getName() : $name; $destination = $overwrite ? $targetPath . $name : $this->getDestination($targetPath . $name); - if ( ! @move_uploaded_file($this->path, $destination)) + try + { + @move_uploaded_file($this->path, $destination); + } + catch (\Exception $e) { $error = error_get_last(); - throw new \RuntimeException(sprintf('Could not move file %s to %s (%s)', basename($this->path), $targetPath, strip_tags($error['message']))); + throw HTTPException::forMoveFailed(basename($this->path), $targetPath, strip_tags($error['message'])); } @chmod($targetPath, 0777 & ~umask()); // Success, so store our new information - $this->path = $targetPath; - $this->name = basename($destination); - $this->hasMoved = true; + $this->path = $targetPath; + $this->name = basename($destination); + $this->hasMoved = true; return true; } @@ -193,20 +199,20 @@ public function move(string $targetPath, string $name = null, bool $overwrite = * * @return string The path set or created. */ - protected function setPath($path) - { - if (!is_dir($path)) - { - mkdir($path, 0777, true); - //create the index.html file - if (!file_exists($path.'index.html')) - { - $file = fopen($path.'index.html', 'x+'); - fclose($file); - } - } - return $path; - } + protected function setPath($path) + { + if ( ! is_dir($path)) + { + mkdir($path, 0777, true); + //create the index.html file + if ( ! file_exists($path . 'index.html')) + { + $file = fopen($path . 'index.html', 'x+'); + fclose($file); + } + } + return $path; + } //-------------------------------------------------------------------- @@ -260,6 +266,7 @@ public function getError(): int 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.', diff --git a/system/HTTP/IncomingRequest.php b/system/HTTP/IncomingRequest.php index 81fc864fde0a..5b16068bbfec 100755 --- a/system/HTTP/IncomingRequest.php +++ b/system/HTTP/IncomingRequest.php @@ -1,4 +1,6 @@ -files->all(); // return all files } - //-------------------------------------------------------------------- /** @@ -584,10 +586,14 @@ protected function detectURI($protocol, $baseURL) $this->uri->setHost(parse_url($baseURL, PHP_URL_HOST)); $this->uri->setPort(parse_url($baseURL, PHP_URL_PORT)); $this->uri->resolveRelativeURI(parse_url($baseURL, PHP_URL_PATH)); - } - else + } else { - throw FrameworkException::forEmptyBaseURL(); + // @codeCoverageIgnoreStart + if ( ! is_cli()) + { + throw FrameworkException::forEmptyBaseURL(); + } + // @codeCoverageIgnoreEnd } } @@ -601,7 +607,7 @@ protected function detectURI($protocol, $baseURL) * * @return string */ - public function detectPath($protocol) + public function detectPath($protocol = '') { if (empty($protocol)) { @@ -639,25 +645,21 @@ public function detectPath($protocol) */ public function negotiate(string $type, array $supported, bool $strictMatch = false) { - if (is_null($this->negotiate)) + if (is_null($this->negotiator)) { - $this->negotiate = Services::negotiator($this, true); + $this->negotiator = Services::negotiator($this, true); } switch (strtolower($type)) { case 'media': - return $this->negotiate->media($supported, $strictMatch); - break; + return $this->negotiator->media($supported, $strictMatch); case 'charset': - return $this->negotiate->charset($supported); - break; + return $this->negotiator->charset($supported); case 'encoding': - return $this->negotiate->encoding($supported); - break; + return $this->negotiator->encoding($supported); case 'language': - return $this->negotiate->language($supported); - break; + return $this->negotiator->language($supported); } throw HTTPException::forInvalidNegotiationType($type); @@ -686,25 +688,26 @@ protected function parseRequestURI(): string if (isset($_SERVER['SCRIPT_NAME'][0])) { + // strip the script name from the beginning of the URI if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) { $uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME'])); - } - elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) - { - $uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME']))); - } + } elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) + // if the script is nested, strip the parent folder & script from the URI + if (strpos($uri, $_SERVER['SCRIPT_NAME']) > 0) + $uri = (string) substr($uri, strpos($uri, $_SERVER['SCRIPT_NAME']) + strlen($_SERVER['SCRIPT_NAME'])); + elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) > 0) + $uri = (string) substr($uri, strpos($uri, dirname($_SERVER['SCRIPT_NAME']))); } - // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct + // This section ensures that even on servers that require the URI to contain the query string (Nginx) a correct // URI is found, and also fixes the QUERY_STRING getServer var and $_GET array. if (trim($uri, '/') === '' && strncmp($query, '/', 1) === 0) { $query = explode('?', $query, 2); $uri = $query[0]; $_SERVER['QUERY_STRING'] = $query[1] ?? ''; - } - else + } else { $_SERVER['QUERY_STRING'] = $query; } @@ -735,8 +738,7 @@ protected function parseQueryString(): string if (trim($uri, '/') === '') { return ''; - } - elseif (strncmp($uri, '/', 1) === 0) + } elseif (strncmp($uri, '/', 1) === 0) { $uri = explode('?', $uri, 2); $_SERVER['QUERY_STRING'] = $uri[1] ?? ''; diff --git a/system/HTTP/Message.php b/system/HTTP/Message.php index 1d9634e1dd02..663255bc5208 100644 --- a/system/HTTP/Message.php +++ b/system/HTTP/Message.php @@ -1,4 +1,5 @@ -setHeader($header, $_SERVER[$key]); - } - else - { - $this->setHeader($header, ''); - } + $this->setHeader($header, $_SERVER[$key]); // Add us to the header map so we can find them case-insensitively $this->headerMap[strtolower($header)] = $header; @@ -246,13 +240,6 @@ public function getHeaderLine(string $name): string return ''; } - // If there are more than 1 headers with this name, - // then return the value of the first. - if (is_array($this->headers[$orig_name])) - { - return $this->headers[$orig_name][0]->getValueLine(); - } - return $this->headers[$orig_name]->getValueLine(); } @@ -286,10 +273,6 @@ public function setHeader(string $name, $value) { $this->headers[$name] = new Header($name, $value); } - else - { - $this->headers[$name][] = new Header($name, $value); - } return $this; } diff --git a/system/HTTP/Negotiate.php b/system/HTTP/Negotiate.php index 0f23152423f6..7af05b35b701 100644 --- a/system/HTTP/Negotiate.php +++ b/system/HTTP/Negotiate.php @@ -213,7 +213,7 @@ protected function getBestMatch(array $supported, string $header = null, bool $e // If no acceptable values exist, return the // first that we support. - if (empty($acceptable)) + if (count($acceptable) === 0) { return $supported[0]; } diff --git a/system/HTTP/Request.php b/system/HTTP/Request.php index 139a457efed2..37fd8573a0d0 100644 --- a/system/HTTP/Request.php +++ b/system/HTTP/Request.php @@ -131,7 +131,7 @@ public function getIPAddress(): string if ($spoof) { - for ($i = 0, $c = count($this->proxyIPs); $i < $c; $i ++ ) + for ($i = 0, $c = count($proxy_ips); $i < $c; $i ++ ) { // Check if we have an IP address or a subnet if (strpos($proxy_ips[$i], '/') === FALSE) diff --git a/system/HTTP/Response.php b/system/HTTP/Response.php index 44791bbc45c1..19ecf588e7d0 100644 --- a/system/HTTP/Response.php +++ b/system/HTTP/Response.php @@ -35,11 +35,10 @@ * @since Version 3.0.0 * @filesource */ -use CodeIgniter\HTTP\Exceptions\HTTPException; -use CodeIgniter\Services; use Config\App; -use Config\Format; use Config\Mimes; +use Config\Format; +use CodeIgniter\HTTP\Exceptions\HTTPException; /** * Redirect exception @@ -421,8 +420,6 @@ public function setJSON($body) $this->body = $this->formatBody($body, 'json'); return $this; - - return $this; } //-------------------------------------------------------------------- @@ -438,7 +435,7 @@ public function getJSON() if ($this->bodyFormat != 'json') { - $config = new Format(); + $config = config(Format::class); $formatter = $config->getFormatter('application/json'); $body = $formatter->format($body); @@ -476,7 +473,7 @@ public function getXML() if ($this->bodyFormat != 'xml') { - $config = new Format(); + $config = config(Format::class); $formatter = $config->getFormatter('application/xml'); $body = $formatter->format($body); @@ -505,7 +502,7 @@ protected function formatBody($body, string $format) // Nothing much to do for a string... if (! is_string($body)) { - $config = new Format(); + $config = config(Format::class); $formatter = $config->getFormatter($mime); $body = $formatter->format($body); @@ -660,7 +657,7 @@ public function send() public function sendHeaders() { // Have the headers already been sent? - if (headers_sent()) + if ($this->pretend || headers_sent()) { return $this; } diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 56ac99f02edf..cd82a884f6e4 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -1,4 +1,5 @@ -getScheme(), $this->getAuthority(), $this->getPath(), // Absolute URIs should use a "/" for an empty path - $this->getQuery(), $this->getFragment() + $this->getQuery(), $this->getFragment() ); } @@ -704,7 +705,10 @@ public function setQuery(string $query) continue; } - $parts[$key] = $value; + // URL Decode the value to protect + // from double-encoding a URL. + // Especially useful with the Pager. + $parts[$key] = $this->decode($value); } $this->query = $parts; @@ -714,6 +718,29 @@ public function setQuery(string $query) //-------------------------------------------------------------------- + /** + * Checks the value to see if it has been urlencoded and decodes it if so. + * The urlencode check is not perfect but should catch most cases. + * + * @param string $value + * + * @return string + */ + protected function decode(string $value) + { + if (empty($value)) + { + return $value; + } + + $decoded = urldecode($value); + + // This won't catch all cases, specifically + // changing ' ' to '+' has the same length + // but doesn't really matter for our cases here. + return strlen($decoded) < strlen($value) ? $decoded : $value; + } + /** * Split a query value into it's key/value elements, if both * are present. @@ -870,7 +897,8 @@ protected function filterPath(string $path = null) // Encode characters $path = preg_replace_callback( - '/(?:[^' . self::CHAR_UNRESERVED . ':@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/', function(array $matches) { + '/(?:[^' . self::CHAR_UNRESERVED . ':@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/', function(array $matches) + { return rawurlencode($matches[0]); }, $path ); @@ -923,13 +951,8 @@ protected function applyParts($parts) { if ( ! is_null($parts['port'])) { + // Valid port numbers are enforced by earlier parse_url or setPort() $port = (int) $parts['port']; - - if (1 > $port || 0xffff < $port) - { - throw HTTPException::forInvalidPort($port); - } - $this->port = $port; } } @@ -998,7 +1021,7 @@ public function resolveRelativeURI(string $uri) } else { - if (substr($relative->getPath(), 0, 1) == '/') + if (strpos($relative->getPath(), '/') === 0) { $transformed->setPath($relative->getPath()); } @@ -1107,7 +1130,7 @@ public function removeDotSegments(string $path): string if ($output != '/') { // Add leading slash if necessary - if (substr($path, 0, 1) == '/') + if (strpos($path, '/') === 0) { $output = '/' . $output; } diff --git a/system/HTTP/UserAgent.php b/system/HTTP/UserAgent.php index 94684cce96f6..91ab2609e9b9 100644 --- a/system/HTTP/UserAgent.php +++ b/system/HTTP/UserAgent.php @@ -349,7 +349,7 @@ protected function compileData() */ protected function setPlatform() { - if (is_array($this->config->platforms) && count($this->config->platforms) > 0) + if (is_array($this->config->platforms) && $this->config->platforms) { foreach ($this->config->platforms as $key => $val) { @@ -376,7 +376,7 @@ protected function setPlatform() */ protected function setBrowser() { - if (is_array($this->config->browsers) && count($this->config->browsers) > 0) + if (is_array($this->config->browsers) && $this->config->browsers) { foreach ($this->config->browsers as $key => $val) { @@ -404,7 +404,7 @@ protected function setBrowser() */ protected function setRobot() { - if (is_array($this->config->robots) && count($this->config->robots) > 0) + if (is_array($this->config->robots) && $this->config->robots) { foreach ($this->config->robots as $key => $val) { @@ -431,7 +431,7 @@ protected function setRobot() */ protected function setMobile() { - if (is_array($this->config->mobiles) && count($this->config->mobiles) > 0) + if (is_array($this->config->mobiles) && $this->config->mobiles) { foreach ($this->config->mobiles as $key => $val) { diff --git a/system/Helpers/array_helper.php b/system/Helpers/array_helper.php index 76093f52cebe..45ae7e1bb1a4 100644 --- a/system/Helpers/array_helper.php +++ b/system/Helpers/array_helper.php @@ -33,7 +33,7 @@ function dot_array_search(string $index, array $array) function _array_search_dot(array $indexes, array $array) { // Grab the current index - $currentIndex = count($indexes) + $currentIndex = $indexes ? array_shift($indexes) : null; @@ -71,7 +71,7 @@ function _array_search_dot(array $indexes, array $array) } // Do we need to recursively search this value? - if (is_array($array[$currentIndex]) && count($array[$currentIndex])) + if (is_array($array[$currentIndex]) && $array[$currentIndex]) { return _array_search_dot($indexes, $array[$currentIndex]); } diff --git a/system/Helpers/cookie_helper.php b/system/Helpers/cookie_helper.php index 346404b613e8..616ab9a60dbe 100755 --- a/system/Helpers/cookie_helper.php +++ b/system/Helpers/cookie_helper.php @@ -95,7 +95,7 @@ function set_cookie($name, string $value = '', string $expire = '', string $doma */ function get_cookie($index, bool $xssClean = false) { - $app = new \Config\App(); + $app = config(\Config\App::class); $appCookiePrefix = $app->cookiePrefix; $prefix = isset($_COOKIE[$index]) ? '' : $appCookiePrefix; diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index dcb7ab750d7e..a4dca20006cb 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -48,12 +48,12 @@ * Creates the opening portion of the form. * * @param string $action the URI segments of the form destination - * @param array $attributes a key/value pair of attributes + * @param array|string $attributes a key/value pair of attributes, or string representation * @param array $hidden a key/value pair hidden data * * @return string */ - function form_open(string $action = '', array $attributes = [], array $hidden = []): string + function form_open(string $action = '', $attributes = [], array $hidden = []): string { // If no action is provided then set to the current url if ( ! $action) @@ -75,14 +75,14 @@ function form_open(string $action = '', array $attributes = [], array $hidden = } if (stripos($attributes, 'accept-charset=') === false) { - $config = new \Config\App(); + $config = config(\Config\App::class); $attributes .= ' accept-charset="' . strtolower($config->charset) . '"'; } $form = '
\n"; // Add CSRF field if enabled, but leave it out for GET requests and requests to external websites - $before = (new \Config\Filters())->globals['before']; + $before = Services::filters()->getFilters()['before']; if ((in_array('csrf', $before) || array_key_exists('csrf', $before)) && strpos($action, base_url()) !== false && ! stripos($form, 'method="get"') ) @@ -114,16 +114,16 @@ function form_open(string $action = '', array $attributes = [], array $hidden = * Creates the opening portion of the form, but with "multipart/form-data". * * @param string $action The URI segments of the form destination - * @param array $attributes A key/value pair of attributes + * @param array|string $attributes A key/value pair of attributes, or the same as a string * @param array $hidden A key/value pair hidden data * * @return string */ - function form_open_multipart(string $action = '', array $attributes = [], array $hidden = []): string + function form_open_multipart(string $action = '', $attributes = [], array $hidden = []): string { if (is_string($attributes)) { - $attributes .= ' enctype="multipart/form-data"'; + $attributes .= ' enctype="' . esc('multipart/form-data', 'attr') . '"'; } else { @@ -208,9 +208,9 @@ function form_hidden($name, $value, bool $recursing = false): string function form_input($data = '', string $value = '', $extra = '', string $type = 'text'): string { $defaults = [ - 'type' => $type, - 'name' => is_array($data) ? '' : $data, - 'value' => $value, + 'type' => $type, + 'name' => is_array($data) ? '' : $data, + 'value' => $value, ]; return '\n"; @@ -288,9 +288,9 @@ function form_upload($data = '', string $value = '', $extra = ''): string function form_textarea($data = '', string $value = '', $extra = ''): string { $defaults = [ - 'name' => is_array($data) ? '' : $data, - 'cols' => '40', - 'rows' => '10', + 'name' => is_array($data) ? '' : $data, + 'cols' => '40', + 'rows' => '10', ]; if ( ! is_array($data) || ! isset($data['value'])) { @@ -515,9 +515,9 @@ function form_radio($data = '', string $value = '', bool $checked = false, $extr function form_submit($data = '', string $value = '', $extra = ''): string { $defaults = [ - 'type' => 'submit', - 'name' => is_array($data) ? '' : $data, - 'value' => $value, + 'type' => 'submit', + 'name' => is_array($data) ? '' : $data, + 'value' => $value, ]; return '\n"; @@ -542,9 +542,9 @@ function form_submit($data = '', string $value = '', $extra = ''): string function form_reset($data = '', string $value = '', $extra = ''): string { $defaults = [ - 'type' => 'reset', - 'name' => is_array($data) ? '' : $data, - 'value' => $value, + 'type' => 'reset', + 'name' => is_array($data) ? '' : $data, + 'value' => $value, ]; return '\n"; @@ -569,8 +569,8 @@ function form_reset($data = '', string $value = '', $extra = ''): string function form_button($data = '', string $content = '', $extra = ''): string { $defaults = [ - 'name' => is_array($data) ? '' : $data, - 'type' => 'button', + 'name' => is_array($data) ? '' : $data, + 'type' => 'button', ]; if (is_array($data) && isset($data['content'])) @@ -609,7 +609,7 @@ function form_label(string $label_text = '', string $id = '', array $attributes $label .= ' for="' . $id . '"'; } - if (is_array($attributes) && count($attributes) > 0) + if (is_array($attributes) && $attributes) { foreach ($attributes as $key => $val) { @@ -643,10 +643,10 @@ function form_label(string $label_text = '', string $id = '', array $attributes function form_datalist($name, $value, $options) { $data = [ - 'type' => 'text', - 'name' => $name, - 'list' => $name . '_list', - 'value' => $value, + 'type' => 'text', + 'name' => $name, + 'list' => $name . '_list', + 'value' => $value, ]; $out = form_input($data) . "\n"; @@ -868,7 +868,7 @@ function set_checkbox(string $field, string $value = '', bool $default = false): } // Unchecked checkbox and radio inputs are not even submitted by browsers ... - if (! empty($request->getPost()) || ! empty(old($field))) + if ( ! empty($request->getPost()) || ! empty(old($field))) { return ($input === $value) ? ' checked="checked"' : ''; } @@ -904,7 +904,6 @@ function set_radio(string $field, string $value = '', bool $default = false): st // Try any old input data we may have first $input = $request->getOldInput($field); - if ($input === null) { $input = $request->getPost($field) ?? $default; @@ -925,12 +924,15 @@ function set_radio(string $field, string $value = '', bool $default = false): st } // Unchecked checkbox and radio inputs are not even submitted by browsers ... + $result = ''; if ($request->getPost()) { - return ($input === $value) ? ' checked="checked"' : ''; + $result = ($input === $value) ? ' checked="checked"' : ''; } - return ($default === true) ? ' checked="checked"' : ''; + if (empty($result)) + $result = ($default === true) ? ' checked="checked"' : ''; + return $result; } } @@ -962,7 +964,7 @@ function parse_form_attributes($attributes, $default): string unset($attributes[$key]); } } - if (! empty($attributes)) + if ( ! empty($attributes)) { $default = array_merge($default, $attributes); } diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php index ac8d624c9abc..aa8ae6c9434a 100755 --- a/system/Helpers/html_helper.php +++ b/system/Helpers/html_helper.php @@ -97,22 +97,13 @@ function ol(array $list, string $attributes = ''): string * Generates an HTML ordered list from an single or multi-dimensional array. * * @param string $type - * @param array $list + * @param mixed $list * @param string $attributes * @param int $depth * @return string */ - function _list - ( - string $type = 'ul', array $list = [], string $attributes = '', int $depth = 0 - ): string + function _list(string $type = 'ul', $list = [], string $attributes = '', int $depth = 0): string { - // If an array wasn't submitted there's nothing to do... - if ( ! is_array($list)) - { - return $list; - } - // Set the indentation based on the depth $out = str_repeat(' ', $depth) // Write the opening list tag @@ -162,20 +153,16 @@ function _list * * @param mixed $src * @param bool $indexPage - * @param string $attributes + * @param mixed $attributes * @return string */ - function img - ( - $src = '', bool $indexPage = false, string $attributes = '' - ): string + function img($src = '', bool $indexPage = false, $attributes = ''): string { if ( ! is_array($src)) { $src = ['src' => $src]; } - //If there is no alt attribute defined, set it to an empty string. if ( ! isset($src['alt'])) { @@ -223,20 +210,13 @@ function img * xhtml-frame, html4-strict, html4-trans, and html4-frame. * All values are saved in the doctypes config file. * - * @param mixed $type The doctype to be generated + * @param string $type The doctype to be generated * @return string */ - function doctype($type = 'html5'): string + function doctype(string $type = 'html5'): string { - $doctypes = null; - $env = ENVIRONMENT; - $doctypes = Config\DocTypes::$list; - $customDocTypesPath = APPPATH . "Config/{$env}/DocTypes.php"; - if (file_exists($customDocTypesPath)) - { - $customDocTypesNs = "Config\{$env}\DocTypes"; - $doctypes = $customDocTypesNs::$list; - } + $config = new \Config\DocTypes(); + $doctypes = $config->list; return $doctypes[$type] ?? false; } @@ -256,47 +236,30 @@ function doctype($type = 'html5'): string * @param bool $indexPage Should indexPage be added to the JS path * @return string */ - function script_tag - ( - $src = '', bool $indexPage = false - ): string + function script_tag($src = '', bool $indexPage = false): string { $script = ''], ['<', '>', 'phptagopen', 'phptagclose', 'asptagopen', 'asptagclose', 'backslashtmp', 'scriptclose'], $str - ); - - // The highlight_string function requires that the text be surrounded - // by PHP tags, which we will remove later - $str = highlight_string('', true); - - // Remove our artificially added PHP, and the syntax highlighting that came with it - $str = preg_replace( - [ - '/<\?php( | )/i', - '/(.*?)\?><\/span>\n<\/span>\n<\/code>/is', - '/<\/span>/i', - ], [ - '', - "$1\n\n", - '', - ], $str - ); - - // Replace our markers back to PHP tags. - return str_replace( - ['phptagopen', 'phptagclose', 'asptagopen', 'asptagclose', 'backslashtmp', 'scriptclose'], ['<?', '?>', '<%', '%>', '\\', '</script>'], $str - ); - } + /** + * Code Highlighter + * + * Colorizes code strings + * + * @param string $str the text string + * + * @return string + */ + function highlight_code(string $str): string + { + /* The highlight string function encodes and highlights + * brackets so we need them to start raw. + * + * Also replace any existing PHP tags to temporary markers + * so they don't accidentally break the string out of PHP, + * and thus, thwart the highlighting. + */ + $str = str_replace( + ['<', '>', '', '<%', '%>', '\\', ''], ['<', '>', 'phptagopen', 'phptagclose', 'asptagopen', 'asptagclose', 'backslashtmp', 'scriptclose'], $str + ); + + // The highlight_string function requires that the text be surrounded + // by PHP tags, which we will remove later + $str = highlight_string('', true); + + // Remove our artificially added PHP, and the syntax highlighting that came with it + $str = preg_replace( + [ + '/<\?php( | )/i', + '/(.*?)\?><\/span>\n<\/span>\n<\/code>/is', + '/<\/span>/i', + ], [ + '', + "$1\n\n", + '', + ], $str + ); + + // Replace our markers back to PHP tags. + return str_replace( + ['phptagopen', 'phptagclose', 'asptagopen', 'asptagclose', 'backslashtmp', 'scriptclose'], ['<?', '?>', '<%', '%>', '\\', '</script>'], $str + ); + } } @@ -364,22 +365,22 @@ function highlight_code(string $str): string if ( ! function_exists('highlight_phrase')) { - /** - * Phrase Highlighter - * - * Highlights a phrase within a text string - * - * @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_close the closing tag to end the phrase with - * - * @return string - */ - function highlight_phrase(string $str, string $phrase, string $tag_open = '', string $tag_close = ''): string - { - return ($str !== '' && $phrase !== '') ? preg_replace('/(' . preg_quote($phrase, '/') . ')/i', $tag_open . '\\1' . $tag_close, $str) : $str; - } + /** + * Phrase Highlighter + * + * Highlights a phrase within a text string + * + * @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_close the closing tag to end the phrase with + * + * @return string + */ + function highlight_phrase(string $str, string $phrase, string $tag_open = '', string $tag_close = ''): string + { + return ($str !== '' && $phrase !== '') ? preg_replace('/(' . preg_quote($phrase, '/') . ')/i', $tag_open . '\\1' . $tag_close, $str) : $str; + } } @@ -388,36 +389,36 @@ function highlight_phrase(string $str, string $phrase, string $tag_open = 'characterList) || ! is_array($config->characterList)) - { - $array_from = []; - $array_to = []; - - return $str; - } - $array_from = array_keys($config->characterList); - $array_to = array_values($config->characterList); - - unset($config); - } - - return preg_replace($array_from, $array_to, $str); - } + /** + * Convert Accented Foreign Characters to ASCII + * + * @param string $str Input string + * + * @return string + */ + function convert_accented_characters(string $str): string + { + static $array_from, $array_to; + + if ( ! is_array($array_from)) + { + $config = new Config\ForeignCharacters(); + + if (empty($config->characterList) || ! is_array($config->characterList)) + { + $array_from = []; + $array_to = []; + + return $str; + } + $array_from = array_keys($config->characterList); + $array_to = array_values($config->characterList); + + unset($config); + } + + return preg_replace($array_from, $array_to, $str); + } } @@ -426,100 +427,103 @@ function convert_accented_characters(string $str): string if ( ! function_exists('word_wrap')) { - /** - * Word Wrap - * - * Wraps text at the specified character. Maintains the integrity of words. - * Anything placed between {unwrap}{/unwrap} will not be word wrapped, nor - * will URLs. - * - * @param string $str the text string - * @param int $charlim = 76 the number of characters to wrap at - * - * @return string - */ - function word_wrap(string $str, int $charlim = 76): string - { - // Set the character limit - is_numeric($charlim) || $charlim = 76; - - // Reduce multiple spaces - $str = preg_replace('| +|', ' ', $str); - - // Standardize newlines - if (strpos($str, "\r") !== false) - { - $str = str_replace(["\r\n", "\r"], "\n", $str); - } - - // If the current word is surrounded by {unwrap} tags we'll - // strip the entire chunk and replace it with a marker. - $unwrap = []; - - if (preg_match_all('|\{unwrap\}(.+?)\{/unwrap\}|s', $str, $matches)) - { - for ($i = 0, $c = count($matches[0]); $i < $c; $i ++ ) - { - $unwrap[] = $matches[1][$i]; - $str = str_replace($matches[0][$i], '{{unwrapped' . $i . '}}', $str); - } - } - - // Use PHP's native function to do the initial wordwrap. - // We set the cut flag to FALSE so that any individual words that are - // too long get left alone. In the next step we'll deal with them. - $str = wordwrap($str, $charlim, "\n", false); - - // Split the string into individual lines of text and cycle through them - $output = ''; - - foreach (explode("\n", $str) as $line) - { - // Is the line within the allowed character count? - // If so we'll join it to the output and continue - if (mb_strlen($line) <= $charlim) - { - $output .= $line . "\n"; - continue; - } - - $temp = ''; - - while (mb_strlen($line) > $charlim) - { - // If the over-length word is a URL we won't wrap it - if (preg_match('!\[url.+\]|://|www\.!', $line)) - { - break; - } - // Trim the word down - $temp .= mb_substr($line, 0, $charlim - 1); - $line = mb_substr($line, $charlim - 1); - } - - // If $temp contains data it means we had to split up an over-length - // word into smaller chunks so we'll add it back to our current line - if ($temp !== '') - { - $output .= $temp . "\n" . $line . "\n"; - } - else - { - $output .= $line . "\n"; - } - } - - // Put our markers back - if (! empty($unwrap)) - { - foreach ($unwrap as $key => $val) - { - $output = str_replace('{{unwrapped' . $key . '}}', $val, $output); - } - } - - return $output; - } + /** + * Word Wrap + * + * Wraps text at the specified character. Maintains the integrity of words. + * Anything placed between {unwrap}{/unwrap} will not be word wrapped, nor + * will URLs. + * + * @param string $str the text string + * @param int $charlim = 76 the number of characters to wrap at + * + * @return string + */ + function word_wrap(string $str, int $charlim = 76): string + { + // Set the character limit + is_numeric($charlim) || $charlim = 76; + + // Reduce multiple spaces + $str = preg_replace('| +|', ' ', $str); + + // Standardize newlines + if (strpos($str, "\r") !== false) + { + $str = str_replace(["\r\n", "\r"], "\n", $str); + } + + // If the current word is surrounded by {unwrap} tags we'll + // strip the entire chunk and replace it with a marker. + $unwrap = []; + + if (preg_match_all('|\{unwrap\}(.+?)\{/unwrap\}|s', $str, $matches)) + { + for ($i = 0, $c = count($matches[0]); $i < $c; $i ++) + { + $unwrap[] = $matches[1][$i]; + $str = str_replace($matches[0][$i], '{{unwrapped' . $i . '}}', $str); + } + } + + // Use PHP's native function to do the initial wordwrap. + // We set the cut flag to FALSE so that any individual words that are + // too long get left alone. In the next step we'll deal with them. + $str = wordwrap($str, $charlim, "\n", false); + + // Split the string into individual lines of text and cycle through them + $output = ''; + + foreach (explode("\n", $str) as $line) + { + // Is the line within the allowed character count? + // If so we'll join it to the output and continue + if (mb_strlen($line) <= $charlim) + { + $output .= $line . "\n"; + continue; + } + + $temp = ''; + + while (mb_strlen($line) > $charlim) + { + // If the over-length word is a URL we won't wrap it + if (preg_match('!\[url.+\]|://|www\.!', $line)) + { + break; + } + // Trim the word down + $temp .= mb_substr($line, 0, $charlim - 1); + $line = mb_substr($line, $charlim - 1); + } + + // If $temp contains data it means we had to split up an over-length + // word into smaller chunks so we'll add it back to our current line + if ($temp !== '') + { + $output .= $temp . "\n" . $line . "\n"; + } + else + { + $output .= $line . "\n"; + } + } + + // Put our markers back + if ( ! empty($unwrap)) + { + foreach ($unwrap as $key => $val) + { + $output = str_replace('{{unwrapped' . $key . '}}', $val, $output); + } + } + + // remove any trailing newline + $output = rtrim($output); + + return $output; + } } @@ -528,43 +532,43 @@ function word_wrap(string $str, int $charlim = 76): string if ( ! function_exists('ellipsize')) { - /** - * Ellipsize String - * - * This function will strip tags from a string, split it at its max_length and ellipsize - * - * @param string $str String to ellipsize - * @param int $max_length Max length of string - * @param mixed $position int (1|0) or float, .5, .2, etc for position to split - * @param string $ellipsis ellipsis ; Default '...' - * - * @return string Ellipsized string - */ - function ellipsize(string $str, int $max_length, $position = 1, string $ellipsis = '…'): string - { - // Strip tags - $str = trim(strip_tags($str)); - - // Is the string long enough to ellipsize? - if (mb_strlen($str) <= $max_length) - { - return $str; - } - - $beg = mb_substr($str, 0, floor($max_length * $position)); - $position = ($position > 1) ? 1 : $position; - - if ($position === 1) - { - $end = mb_substr($str, 0, -($max_length - mb_strlen($beg))); - } - else - { - $end = mb_substr($str, -($max_length - mb_strlen($beg))); - } - - return $beg . $ellipsis . $end; - } + /** + * Ellipsize String + * + * This function will strip tags from a string, split it at its max_length and ellipsize + * + * @param string $str String to ellipsize + * @param int $max_length Max length of string + * @param mixed $position int (1|0) or float, .5, .2, etc for position to split + * @param string $ellipsis ellipsis ; Default '...' + * + * @return string Ellipsized string + */ + function ellipsize(string $str, int $max_length, $position = 1, string $ellipsis = '…'): string + { + // Strip tags + $str = trim(strip_tags($str)); + + // Is the string long enough to ellipsize? + if (mb_strlen($str) <= $max_length) + { + return $str; + } + + $beg = mb_substr($str, 0, floor($max_length * $position)); + $position = ($position > 1) ? 1 : $position; + + if ($position === 1) + { + $end = mb_substr($str, 0, -($max_length - mb_strlen($beg))); + } + else + { + $end = mb_substr($str, -($max_length - mb_strlen($beg))); + } + + return $beg . $ellipsis . $end; + } } @@ -573,28 +577,28 @@ function ellipsize(string $str, int $max_length, $position = 1, string $ellipsis if ( ! function_exists('strip_slashes')) { - /** - * Strip Slashes - * - * Removes slashes contained in a string or in an array - * - * @param mixed string or array - * - * @return mixed string or array - */ - function strip_slashes($str) - { - if ( ! is_array($str)) - { - return stripslashes($str); - } - foreach ($str as $key => $val) - { - $str[$key] = strip_slashes($val); - } - - return $str; - } + /** + * Strip Slashes + * + * Removes slashes contained in a string or in an array + * + * @param mixed string or array + * + * @return mixed string or array + */ + function strip_slashes($str) + { + if ( ! is_array($str)) + { + return stripslashes($str); + } + foreach ($str as $key => $val) + { + $str[$key] = strip_slashes($val); + } + + return $str; + } } @@ -603,19 +607,19 @@ function strip_slashes($str) if ( ! function_exists('strip_quotes')) { - /** - * Strip Quotes - * - * Removes single and double quotes from a string - * - * @param string - * - * @return string - */ - function strip_quotes(string $str): string - { - return str_replace(['"', "'"], '', $str); - } + /** + * Strip Quotes + * + * Removes single and double quotes from a string + * + * @param string + * + * @return string + */ + function strip_quotes(string $str): string + { + return str_replace(['"', "'"], '', $str); + } } @@ -624,19 +628,19 @@ function strip_quotes(string $str): string if ( ! function_exists('quotes_to_entities')) { - /** - * Quotes to Entities - * - * Converts single and double quotes to entities - * - * @param string - * - * @return string - */ - function quotes_to_entities(string $str): string - { - return str_replace(["\'", "\"", "'", '"'], ["'", """, "'", """], $str); - } + /** + * Quotes to Entities + * + * Converts single and double quotes to entities + * + * @param string + * + * @return string + */ + function quotes_to_entities(string $str): string + { + return str_replace(["\'", "\"", "'", '"'], ["'", """, "'", """], $str); + } } @@ -645,26 +649,26 @@ function quotes_to_entities(string $str): string if ( ! function_exists('reduce_double_slashes')) { - /** - * Reduce Double Slashes - * - * Converts double slashes in a string to a single slash, - * except those found in http:// - * - * http://www.some-site.com//index.php - * - * becomes: - * - * http://www.some-site.com/index.php - * - * @param string - * - * @return string - */ - function reduce_double_slashes(string $str): string - { - return preg_replace('#(^|[^:])//+#', '\\1/', $str); - } + /** + * Reduce Double Slashes + * + * Converts double slashes in a string to a single slash, + * except those found in http:// + * + * http://www.some-site.com//index.php + * + * becomes: + * + * http://www.some-site.com/index.php + * + * @param string + * + * @return string + */ + function reduce_double_slashes(string $str): string + { + return preg_replace('#(^|[^:])//+#', '\\1/', $str); + } } @@ -673,29 +677,29 @@ function reduce_double_slashes(string $str): string if ( ! function_exists('reduce_multiples')) { - /** - * Reduce Multiples - * - * Reduces multiple instances of a particular character. Example: - * - * Fred, Bill,, Joe, Jimmy - * - * becomes: - * - * Fred, Bill, Joe, Jimmy - * - * @param string $str - * @param string $character the character you wish to reduce - * @param bool $trim TRUE/FALSE - whether to trim the character from the beginning/end - * - * @return string - */ - function reduce_multiples(string $str, string $character = ',', bool $trim = false): string - { - $str = preg_replace('#' . preg_quote($character, '#') . '{2,}#', $character, $str); - - return ($trim === true) ? trim($str, $character) : $str; - } + /** + * Reduce Multiples + * + * Reduces multiple instances of a particular character. Example: + * + * Fred, Bill,, Joe, Jimmy + * + * becomes: + * + * Fred, Bill, Joe, Jimmy + * + * @param string $str + * @param string $character the character you wish to reduce + * @param bool $trim TRUE/FALSE - whether to trim the character from the beginning/end + * + * @return string + */ + function reduce_multiples(string $str, string $character = ',', bool $trim = false): string + { + $str = preg_replace('#' . preg_quote($character, '#') . '{2,}#', $character, $str); + + return ($trim === true) ? trim($str, $character) : $str; + } } @@ -704,51 +708,51 @@ function reduce_multiples(string $str, string $character = ',', bool $trim = fal if ( ! function_exists('random_string')) { - /** - * Create a Random String - * - * Useful for generating passwords or hashes. - * - * @param string $type Type of random string. basic, alpha, alnum, numeric, nozero, unique, md5, sha1, and crypto - * @param int $len Number of characters - * - * @return string - */ - function random_string(string $type = 'alnum', int $len = 8): string - { - switch ($type) - { - case 'basic': - return mt_rand(); - case 'alnum': - case 'numeric': - case 'nozero': - case 'alpha': - switch ($type) - { - case 'alpha': - $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - break; - case 'alnum': - $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - break; - case 'numeric': - $pool = '0123456789'; - break; - case 'nozero': - $pool = '123456789'; - break; - } - - return substr(str_shuffle(str_repeat($pool, ceil($len / strlen($pool)))), 0, $len); - case 'md5': - return md5(uniqid(mt_rand(), true)); - case 'sha1': - return sha1(uniqid(mt_rand(), true)); - case 'crypto': - return bin2hex(random_bytes($len/2)); - } - } + /** + * Create a Random String + * + * Useful for generating passwords or hashes. + * + * @param string $type Type of random string. basic, alpha, alnum, numeric, nozero, md5, sha1, and crypto + * @param int $len Number of characters + * + * @return string + */ + function random_string(string $type = 'alnum', int $len = 8): string + { + switch ($type) + { + case 'alnum': + case 'numeric': + case 'nozero': + case 'alpha': + switch ($type) + { + case 'alpha': + $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + break; + case 'alnum': + $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + break; + case 'numeric': + $pool = '0123456789'; + break; + case 'nozero': + $pool = '123456789'; + break; + } + + return substr(str_shuffle(str_repeat($pool, ceil($len / strlen($pool)))), 0, $len); + case 'md5': + return md5(uniqid(mt_rand(), true)); + case 'sha1': + return sha1(uniqid(mt_rand(), true)); + case 'crypto': + return bin2hex(random_bytes($len / 2)); + } + // 'basic' type treated as default + return (string) mt_rand(); + } } @@ -757,21 +761,21 @@ function random_string(string $type = 'alnum', int $len = 8): string if ( ! function_exists('increment_string')) { - /** - * Add's _1 to a string or increment the ending number to allow _2, _3, etc - * - * @param string $str Required - * @param string $separator What should the duplicate number be appended with - * @param int $first Which number should be used for the first dupe increment - * - * @return string - */ - function increment_string(string $str, string $separator = '_', int $first = 1): string - { - preg_match('/(.+)' . preg_quote($separator, '/') . '([0-9]+)$/', $str, $match); - - return isset($match[2]) ? $match[1] . $separator . ($match[2] + 1) : $str . $separator . $first; - } + /** + * Add's _1 to a string or increment the ending number to allow _2, _3, etc + * + * @param string $str Required + * @param string $separator What should the duplicate number be appended with + * @param int $first Which number should be used for the first dupe increment + * + * @return string + */ + function increment_string(string $str, string $separator = '_', int $first = 1): string + { + preg_match('/(.+)' . preg_quote($separator, '/') . '([0-9]+)$/', $str, $match); + + return isset($match[2]) ? $match[1] . $separator . ($match[2] + 1) : $str . $separator . $first; + } } @@ -780,30 +784,30 @@ function increment_string(string $str, string $separator = '_', int $first = 1): if ( ! function_exists('alternator')) { - /** - * Alternator - * - * Allows strings to be alternated. See docs... - * - * @param string (as many parameters as needed) - * - * @return string - */ - function alternator(): string - { - static $i; + /** + * Alternator + * + * Allows strings to be alternated. See docs... + * + * @param string (as many parameters as needed) + * + * @return string + */ + function alternator(): string + { + static $i; - if (func_num_args() === 0) - { - $i = 0; + if (func_num_args() === 0) + { + $i = 0; - return ''; - } + return ''; + } - $args = func_get_args(); + $args = func_get_args(); - return $args[($i ++ % count($args))]; - } + return $args[($i ++ % count($args))]; + } } @@ -812,65 +816,65 @@ function alternator(): string if ( ! function_exists('excerpt')) { - /** - * Excerpt. - * - * Allows to extract a piece of text surrounding a word or phrase. - * - * @param string $text String to search the phrase - * @param string $phrase Phrase that will be searched for. - * @param int $radius The amount of characters returned arround 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. - */ - function excerpt(string $text, string $phrase = null, int $radius = 100, string $ellipsis = '...'): string - { - if (isset($phrase)) - { - $phrasePos = strpos(strtolower($text), strtolower($phrase)); - $phraseLen = strlen($phrase); - } - elseif ( ! isset($phrase)) - { - $phrasePos = $radius / 2; - $phraseLen = 1; - } - - $pre = explode(' ', substr($text, 0, $phrasePos)); - $pos = explode(' ', substr($text, $phrasePos + $phraseLen)); - - $prev = ' '; - $post = ' '; - $count = 0; - - foreach (array_reverse($pre) as $pr => $e) - { - if ((strlen($e) + $count + 1) < $radius) - { - $prev = ' ' . $e . $prev; - } - $count = ++ $count + strlen($e); - } - - $count = 0; - - foreach ($pos as $po => $s) - { - if ((strlen($s) + $count + 1) < $radius) - { - $post .= $s . ' '; - } - $count = ++ $count + strlen($s); - } - - $ellPre = $phrase ? $ellipsis : ''; - - return str_replace(' ', ' ', $ellPre . $prev . $phrase . $post . $ellipsis); - } - - //-------------------------------------------------------------------- + /** + * Excerpt. + * + * Allows to extract a piece of text surrounding a word or phrase. + * + * @param string $text String to search the phrase + * @param string $phrase Phrase that will be searched for. + * @param int $radius The amount of characters returned arround 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. + */ + function excerpt(string $text, string $phrase = null, int $radius = 100, string $ellipsis = '...'): string + { + if (isset($phrase)) + { + $phrasePos = strpos(strtolower($text), strtolower($phrase)); + $phraseLen = strlen($phrase); + } + elseif ( ! isset($phrase)) + { + $phrasePos = $radius / 2; + $phraseLen = 1; + } + + $pre = explode(' ', substr($text, 0, $phrasePos)); + $pos = explode(' ', substr($text, $phrasePos + $phraseLen)); + + $prev = ' '; + $post = ' '; + $count = 0; + + foreach (array_reverse($pre) as $pr => $e) + { + if ((strlen($e) + $count + 1) < $radius) + { + $prev = ' ' . $e . $prev; + } + $count = ++ $count + strlen($e); + } + + $count = 0; + + foreach ($pos as $po => $s) + { + if ((strlen($s) + $count + 1) < $radius) + { + $post .= $s . ' '; + } + $count = ++ $count + strlen($s); + } + + $ellPre = $phrase ? $ellipsis : ''; + + return str_replace(' ', ' ', $ellPre . $prev . $phrase . $post . $ellipsis); + } + + //-------------------------------------------------------------------- } diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index e45fbe8b8fc5..50145dad2d56 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -55,7 +55,7 @@ function site_url($path = '', string $scheme = null, \Config\App $altConfig = nu } // use alternate config if provided, else default one - $config = empty($altConfig) ? new \Config\App() : $altConfig; + $config = empty($altConfig) ? config(\Config\App::class) : $altConfig; $base = base_url(); @@ -102,21 +102,11 @@ function base_url($path = '', string $scheme = null): string $path = implode('/', $path); } - // We should be using the set baseURL the user set - // otherwise get rid of the path because we have + // We should be using the configured baseURL that the user set; + // otherwise get rid of the path, because we have // no way of knowing the intent... $config = \CodeIgniter\Config\Services::request()->config; - - if ( ! empty($config->baseURL)) - { - $url = new \CodeIgniter\HTTP\URI($config->baseURL); - } - else - { - $url = \CodeIgniter\Config\Services::request($config, false)->uri; - $url->setPath('/'); - } - + $url = new \CodeIgniter\HTTP\URI($config->baseURL); unset($config); // Merge in the path set by the user, if any @@ -228,7 +218,7 @@ function uri_string(): string function index_page(\Config\App $altConfig = null): string { // use alternate config if provided, else default one - $config = empty($altConfig) ? new \Config\App() : $altConfig; + $config = empty($altConfig) ? config(\Config\App::class) : $altConfig; return $config->indexPage; } @@ -255,7 +245,7 @@ function index_page(\Config\App $altConfig = null): string function anchor($uri = '', $title = '', $attributes = '', \Config\App $altConfig = null): string { // use alternate config if provided, else default one - $config = empty($altConfig) ? new \Config\App() : $altConfig; + $config = empty($altConfig) ? config(\Config\App::class) : $altConfig; $title = (string) $title; @@ -299,7 +289,7 @@ function anchor($uri = '', $title = '', $attributes = '', \Config\App $altConfig function anchor_popup($uri = '', $title = '', $attributes = false, \Config\App $altConfig = null): string { // use alternate config if provided, else default one - $config = empty($altConfig) ? new \Config\App() : $altConfig; + $config = empty($altConfig) ? config(\Config\App::class) : $altConfig; $title = (string) $title; $site_url = preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, '', $config); @@ -402,7 +392,7 @@ function safe_mailto($email, $title = '', $attributes = ''): string $x = str_split('" - . "//" . ''; return $output; @@ -605,10 +593,10 @@ function url_title($str, $separator = '-', $lowercase = false): string $q_separator = preg_quote($separator, '#'); $trans = [ - '&.+?;' => '', - '[^\w\d _-]' => '', - '\s+' => $separator, - '(' . $q_separator . ')+' => $separator + '&.+?;' => '', + '[^\w\d _-]' => '', + '\s+' => $separator, + '(' . $q_separator . ')+' => $separator ]; $str = strip_tags($str); diff --git a/system/Images/Handlers/ImageMagickHandler.php b/system/Images/Handlers/ImageMagickHandler.php index a72073090974..ebf83aafd8fe 100644 --- a/system/Images/Handlers/ImageMagickHandler.php +++ b/system/Images/Handlers/ImageMagickHandler.php @@ -44,9 +44,9 @@ * To make this library as compatible as possible with the broadest * number of installations, we do not use the Imagick extension, * but simply use the command line version. - * + * * hmm - the width & height accessors at the end use the imagick extension. - * + * * FIXME - This needs conversion & unit testing, to use the imagick extension * * @package CodeIgniter\Images\Handlers @@ -87,7 +87,7 @@ public function _resize(bool $maintainRatio = false) //todo FIX THIS HANDLER PROPERLY $escape = "\\"; - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') + if (stripos(PHP_OS, 'WIN') === 0) { $escape = ""; } @@ -421,7 +421,7 @@ protected function _text(string $text, array $options = []) } //-------------------------------------------------------------------- - + //-------------------------------------------------------------------- public function _getWidth() diff --git a/system/Language/Language.php b/system/Language/Language.php index 96cbdf73e37d..b43d44bc1a5c 100644 --- a/system/Language/Language.php +++ b/system/Language/Language.php @@ -174,7 +174,7 @@ protected function parseLine(string $line): array */ protected function formatMessage($message, array $args = []) { - if ( ! $this->intlSupport || ! count($args)) + if ( ! $this->intlSupport || ! $args) { return $message; } diff --git a/system/Language/en/HTTP.php b/system/Language/en/HTTP.php index 49bf72fb1b1d..7229de3a6c34 100644 --- a/system/Language/en/HTTP.php +++ b/system/Language/en/HTTP.php @@ -51,4 +51,9 @@ // CSRF 'disallowedAction' => 'The action you requested is not allowed.', + + // Uploaded file moving + '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})', ]; diff --git a/system/Language/en/Migrations.php b/system/Language/en/Migrations.php index 98e52cb39e78..2f498d5f2a9a 100644 --- a/system/Language/en/Migrations.php +++ b/system/Language/en/Migrations.php @@ -45,7 +45,6 @@ 'on' => 'Migrated On: ', 'migSeeder' => 'Seeder name', 'migMissingSeeder' => 'You must provide a seeder name.', - 'historyFor' => 'Migration history For ', 'removed' => 'Rolling back: ', 'added' => 'Running: ', diff --git a/system/Log/Logger.php b/system/Log/Logger.php index 4de39d1bc869..c433e0e181b6 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -155,7 +155,7 @@ public function __construct($config, bool $debug = CI_DEBUG) // Now convert loggable levels to strings. // We only use numbers to make the threshold setting convenient for users. - if (count($this->loggableLevels)) + if ($this->loggableLevels) { $temp = []; foreach ($this->loggableLevels as $level) @@ -364,10 +364,14 @@ public function log($level, $message, array $context = []): bool foreach ($this->handlerConfig as $className => $config) { + if ( ! array_key_exists($className, $this->handlers)) { + $this->handlers[$className] = new $className($config); + } + /** * @var \CodeIgniter\Log\Handlers\HandlerInterface */ - $handler = new $className($config); + $handler = $this->handlers[$className]; if ( ! $handler->canHandle($level)) { @@ -444,7 +448,7 @@ protected function interpolate($message, array $context = []) { preg_match('/env:[^}]+/', $message, $matches); - if (count($matches)) + if ($matches) { foreach ($matches as $str) { diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 4327124e6bd1..517b236ae5e2 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -718,7 +718,7 @@ public function group($name, ...$params) $callback = array_pop($params); - if (count($params) && is_array($params[0])) + if ($params && is_array($params[0])) { $this->currentOptions = array_shift($params); } diff --git a/system/Test/FeatureTestCase.php b/system/Test/FeatureTestCase.php index 714375b4056f..1a596a59873b 100644 --- a/system/Test/FeatureTestCase.php +++ b/system/Test/FeatureTestCase.php @@ -5,6 +5,7 @@ use CodeIgniter\Events\Events; use CodeIgniter\HTTP\UserAgent; use CodeIgniter\HTTP\IncomingRequest; +use Config\App; /** * Class FeatureTestCase @@ -42,7 +43,7 @@ protected function withRoutes(array $routes = null) { $collection = \Config\Services::routes(); - if (count($routes)) + if ($routes) { foreach ($routes as $route) { @@ -212,7 +213,7 @@ public function options(string $path, array $params = null) */ protected function setupRequest(string $method, string $path=null, $params = null) { - $config = new \Config\App(); + $config = config(App::class); $uri = new URI($config->baseURL .'/'. trim($path, '/ ')); $request = new IncomingRequest($config, clone($uri), $params, new UserAgent()); diff --git a/system/Validation/CreditCardRules.php b/system/Validation/CreditCardRules.php index 23177c7797f6..0cfb82e39789 100644 --- a/system/Validation/CreditCardRules.php +++ b/system/Validation/CreditCardRules.php @@ -74,7 +74,7 @@ class CreditCardRules 'UATP' => ['name' => 'uatp', 'length' => '15', 'prefixes' => '1', 'checkdigit' => true], 'Verve' => ['name' => 'verve', 'length' => '16,19', 'prefixes' => '506,650', 'checkdigit' => true], 'Visa' => ['name' => 'visa', 'length' => '13,16,19', 'prefixes' => '4', 'checkdigit' => true], - // Canadian Cards + // Canadian Cards 'BMO ABM Card' => ['name' => 'bmoabm', 'length' => '16', 'prefixes' => '500', 'checkdigit' => false], 'CIBC Convenience Card' => ['name' => 'cibc', 'length' => '16', 'prefixes' => '4506', 'checkdigit' => false], 'HSBC Canada Card' => ['name' => 'hsbc', 'length' => '16', 'prefixes' => '56', 'checkdigit' => false], @@ -151,7 +151,7 @@ public function valid_cc_number(string $ccNumber = null, string $type, array $da foreach ($prefixes as $prefix) { - if ($prefix == mb_substr($ccNumber, 0, mb_strlen($prefix))) + if (mb_strpos($ccNumber, $prefix) === 0) { $validPrefix = true; } diff --git a/system/Validation/FormatRules.php b/system/Validation/FormatRules.php index 4b5fea2dce12..94beb66cc9b3 100644 --- a/system/Validation/FormatRules.php +++ b/system/Validation/FormatRules.php @@ -198,7 +198,7 @@ public function numeric(string $str = null): bool */ public function regex_match(string $str = null, string $pattern, array $data): bool { - if (substr($pattern, 0, 1) != '/') + if (strpos($pattern, '/') !== 0) { $pattern = "/{$pattern}/"; } diff --git a/system/View/Parser.php b/system/View/Parser.php index 069ca1733d21..13ae9139e6fd 100644 --- a/system/View/Parser.php +++ b/system/View/Parser.php @@ -297,13 +297,13 @@ protected function parse(string $template, array $data = [], array $options = nu } //-------------------------------------------------------------------- -//FIXME the following method does not appear to be used anywhere, so commented out +//FIXME the following method does not appear to be used anywhere, so commented out // protected function is_assoc($arr) // { // return array_keys($arr) !== range(0, count($arr) - 1); // } //-------------------------------------------------------------------- -//FIXME the following method does not appear to be used anywhere, so commented out +//FIXME the following method does not appear to be used anywhere, so commented out // function strpos_all($haystack, $needle) // { // $offset = 0; @@ -571,7 +571,7 @@ protected function replaceSingle($pattern, $content, $template, bool $escape = f $template = preg_replace_callback($pattern, function ($matches) use ($content, $escape) { // Check for {! !} syntax to not-escape this one. - if (substr($matches[0], 0, 2) == '{!' && substr($matches[0], -2) == '!}') + if (strpos($matches[0], '{!') === 0 && substr($matches[0], -2) == '!}') { $escape = false; } diff --git a/system/View/View.php b/system/View/View.php index 1ec2066bb21a..2552f4324947 100644 --- a/system/View/View.php +++ b/system/View/View.php @@ -215,7 +215,7 @@ public function render(string $view, array $options = null, $saveData = null): s $after = (new \Config\Filters())->globals['after']; if (in_array('toolbar', $after) || array_key_exists('toolbar', $after)) { - $toolbarCollectors = (new \Config\App())->toolbarCollectors; + $toolbarCollectors = (config(\Config\App::class))->toolbarCollectors; if (in_array('CodeIgniter\Debug\Toolbar\Collectors\Views', $toolbarCollectors) || array_key_exists('CodeIgniter\Debug\Toolbar\Collectors\Views', $toolbarCollectors)) { // Clean up our path names to make them a little cleaner diff --git a/system/bootstrap.php b/system/bootstrap.php index b5dea81f3cab..b8059ad857fc 100644 --- a/system/bootstrap.php +++ b/system/bootstrap.php @@ -151,7 +151,8 @@ class_alias('Config\Services', 'CodeIgniter\Services'); * the pieces all working together. */ -$app = new \CodeIgniter\CodeIgniter(new \Config\App()); +$appConfig = config(\Config\App::class); +$app = new \CodeIgniter\CodeIgniter($appConfig); $app->initialize(); return $app; diff --git a/tests/_support/Config/MockAppConfig.php b/tests/_support/Config/MockAppConfig.php index 11672cae2f7f..753eb986d8be 100644 --- a/tests/_support/Config/MockAppConfig.php +++ b/tests/_support/Config/MockAppConfig.php @@ -20,6 +20,7 @@ class MockAppConfig public $CSRFExpire = 7200; public $CSRFRegenerate = true; public $CSRFExcludeURIs = ['http://example.com']; + public $CSRFRedirect = false; public $CSPEnabled = false; diff --git a/tests/_support/HTTP/MockIncomingRequest.php b/tests/_support/HTTP/MockIncomingRequest.php index f6592e728e7c..0812135259b2 100644 --- a/tests/_support/HTTP/MockIncomingRequest.php +++ b/tests/_support/HTTP/MockIncomingRequest.php @@ -4,10 +4,10 @@ class MockIncomingRequest extends IncomingRequest { - public function populateHeaders() - { - // Don't do anything... force the tester to manually set the headers they want. - } +// public function populateHeaders() +// { +// // Don't do anything... force the tester to manually set the headers they want. +// } public function detectURI($protocol, $baseURL) { diff --git a/tests/_support/Images/ci-logo.png b/tests/_support/Images/ci-logo.png index a61f163022b7..34fb01082893 100644 Binary files a/tests/_support/Images/ci-logo.png and b/tests/_support/Images/ci-logo.png differ diff --git a/tests/system/CLI/CommandRunnerTest.php b/tests/system/CLI/CommandRunnerTest.php index 9802507712ab..16f7b82566da 100644 --- a/tests/system/CLI/CommandRunnerTest.php +++ b/tests/system/CLI/CommandRunnerTest.php @@ -1,6 +1,7 @@ config = new MockCLIConfig(); $this->request = new \CodeIgniter\HTTP\IncomingRequest($this->config, new \CodeIgniter\HTTP\URI('https://somwhere.com'), null, new UserAgent()); $this->response = new \CodeIgniter\HTTP\Response($this->config); - $this->runner = new CommandRunner($this->request, $this->response); + $this->logger = Services::logger(); + $this->runner = new CommandRunner(); + $this->runner->initController($this->request, $this->response, $this->logger); } public function tearDown() diff --git a/tests/system/CLI/ConsoleTest.php b/tests/system/CLI/ConsoleTest.php index c06c6c5b0abd..0a1068706b80 100644 --- a/tests/system/CLI/ConsoleTest.php +++ b/tests/system/CLI/ConsoleTest.php @@ -54,7 +54,7 @@ public function testHeader() public function testRun() { $console = new \CodeIgniter\CLI\Console($this->app); - $console->run(); + $console->run(true); $result = CITestStreamFilter::$buffer; // make sure the result looks like a command list diff --git a/tests/system/Cache/Handlers/RedisHandlerTest.php b/tests/system/Cache/Handlers/RedisHandlerTest.php index fcdc9baf27e0..88c39bf57cf6 100644 --- a/tests/system/Cache/Handlers/RedisHandlerTest.php +++ b/tests/system/Cache/Handlers/RedisHandlerTest.php @@ -79,7 +79,7 @@ public function testNew() public function testDestruct() { - $this->redisHandler = new RedisHandler($this->config->redis); + $this->redisHandler = new RedisHandler($this->config); $this->redisHandler->initialize(); $this->assertInstanceOf(RedisHandler::class, $this->redisHandler); diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index f098425dd3c2..6579f444ca9d 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -42,7 +42,7 @@ public function testRunDefaultRoute() $_SERVER['argc'] = 2; ob_start(); - $this->codeigniter->run(); + $this->codeigniter->useSafeOutput(true)->run(); $output = ob_get_clean(); $this->assertContains('

Welcome to CodeIgniter

', $output); @@ -58,7 +58,7 @@ public function testRunEmptyDefaultRoute() $_SERVER['argc'] = 1; ob_start(); - $this->codeigniter->run(); + $this->codeigniter->useSafeOutput(true)->run(); $output = ob_get_clean(); $this->assertContains('

Welcome to CodeIgniter

', $output); @@ -84,7 +84,7 @@ public function testRunClosureRoute() Services::injectMock('router', $router); ob_start(); - $this->codeigniter->run(); + $this->codeigniter->useSafeOutput(true)->run(); $output = ob_get_clean(); $this->assertContains('You want to see "about" page.', $output); @@ -108,7 +108,7 @@ public function testRun404Override() Services::injectMock('router', $router); ob_start(); - $this->codeigniter->run(); + $this->codeigniter->useSafeOutput(true)->run(); $output = ob_get_clean(); $this->assertContains('

Welcome to CodeIgniter

', $output); @@ -134,7 +134,7 @@ public function testRun404OverrideByClosure() Services::injectMock('router', $router); ob_start(); - $this->codeigniter->run($routes); + $this->codeigniter->useSafeOutput(true)->run($routes); $output = ob_get_clean(); $this->assertContains('404 Override by Closure.', $output); @@ -160,7 +160,7 @@ public function testControllersCanReturnString() Services::injectMock('router', $router); ob_start(); - $this->codeigniter->run(); + $this->codeigniter->useSafeOutput(true)->run(); $output = ob_get_clean(); $this->assertContains('You want to see "about" page.', $output); @@ -188,7 +188,7 @@ public function testControllersCanReturnResponseObject() Services::injectMock('router', $router); ob_start(); - $this->codeigniter->run(); + $this->codeigniter->useSafeOutput(true)->run(); $output = ob_get_clean(); $this->assertContains("You want to see 'about' page.", $output); @@ -224,7 +224,7 @@ public function testRoutesIsEmpty() Services::injectMock('router', $router); ob_start(); - $this->codeigniter->run(); + $this->codeigniter->useSafeOutput(true)->run(); $output = ob_get_clean(); $this->assertContains('

Welcome to CodeIgniter

', $output); @@ -240,7 +240,7 @@ public function testTransfersCorrectHTTPVersion() $_SERVER['SERVER_PROTOCOL'] = 'HTTP/2'; ob_start(); - $this->codeigniter->run(); + $this->codeigniter->useSafeOutput(true)->run(); $output = ob_get_clean(); $response = $this->getPrivateProperty($this->codeigniter, 'response'); diff --git a/tests/system/Commands/CommandsTest.php b/tests/system/Commands/CommandsTest.php index 00dd6439d665..e51f77fe52a1 100644 --- a/tests/system/Commands/CommandsTest.php +++ b/tests/system/Commands/CommandsTest.php @@ -1,18 +1,29 @@ stream_filter = stream_filter_append(STDOUT, 'CITestStreamFilter'); @@ -32,7 +43,9 @@ public function setUp() $this->config = new MockAppConfig(); $this->request = new \CodeIgniter\HTTP\IncomingRequest($this->config, new \CodeIgniter\HTTP\URI('https://somwhere.com'), null, new UserAgent()); $this->response = new \CodeIgniter\HTTP\Response($this->config); - $this->runner = new CommandRunner($this->request, $this->response); + $this->logger = Services::logger(); + $this->runner = new CommandRunner(); + $this->runner->initController($this->request, $this->response, $this->logger); } public function tearDown() diff --git a/tests/system/Commands/SessionsCommandsTest.php b/tests/system/Commands/SessionsCommandsTest.php index 7171f8e8ad17..f4b619566928 100644 --- a/tests/system/Commands/SessionsCommandsTest.php +++ b/tests/system/Commands/SessionsCommandsTest.php @@ -1,17 +1,27 @@ stream_filter = stream_filter_append(STDOUT, 'CITestStreamFilter'); @@ -31,7 +41,9 @@ public function setUp() $this->config = new MockAppConfig(); $this->request = new \CodeIgniter\HTTP\IncomingRequest($this->config, new \CodeIgniter\HTTP\URI('https://somwhere.com'), null, new UserAgent()); $this->response = new \CodeIgniter\HTTP\Response($this->config); - $this->runner = new CommandRunner($this->request, $this->response); + $this->logger = Services::logger(); + $this->runner = new CommandRunner(); + $this->runner->initController($this->request, $this->response, $this->logger); } public function tearDown() diff --git a/tests/system/Config/ConfigTest.php b/tests/system/Config/ConfigTest.php index b1e8397fffe4..ea08b26e981f 100644 --- a/tests/system/Config/ConfigTest.php +++ b/tests/system/Config/ConfigTest.php @@ -7,12 +7,10 @@ class ConfigTest extends \CIUnitTestCase public function testCreateSingleInstance() { - $Config = Config::get('email', false); - $UpperConfig = Config::get('Email', false); + $Config = Config::get('Email', false); $NamespaceConfig = Config::get('Config\\Email', false); $this->assertInstanceOf(Email::class, $Config); - $this->assertInstanceOf(Email::class, $UpperConfig); $this->assertInstanceOf(Email::class, $NamespaceConfig); } @@ -25,7 +23,7 @@ public function testCreateInvalidInstance() public function testCreateSharedInstance() { - $Config = Config::get('email' ); + $Config = Config::get('Email' ); $Config2 = Config::get('Config\\Email'); $this->assertTrue($Config === $Config2); @@ -33,7 +31,7 @@ public function testCreateSharedInstance() public function testCreateNonConfig() { - $Config = Config::get('constants', false); + $Config = Config::get('Constants', false); $this->assertNull($Config); } diff --git a/tests/system/Config/DotEnvTest.php b/tests/system/Config/DotEnvTest.php index ca3638cdd764..050ae1e5c0f0 100644 --- a/tests/system/Config/DotEnvTest.php +++ b/tests/system/Config/DotEnvTest.php @@ -1,4 +1,8 @@ -fixturesFolder = __DIR__.'/fixtures'; - $file = "unreadable.env"; - $path = rtrim($this->fixturesFolder, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file; - chmod($path, 0644); + $this->root = vfsStream::setup(); + $this->fixturesFolder = $this->root->url(); + $this->path = TESTPATH . 'system/Config/fixtures'; + vfsStream::copyFromFileSystem($this->path, $this->root); + + $file = "unreadable.env"; + $path = rtrim($this->fixturesFolder, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file; + chmod($path, 0644); + } + + public function tearDown() + { + parent::tearDown(); + + $this->root = null; } //-------------------------------------------------------------------- public function testReturnsFalseIfCannotFindFile() { - $dotenv = new DotEnv(__DIR__); + $dotenv = new DotEnv($this->fixturesFolder, 'bogus'); $this->assertFalse($dotenv->load()); } @@ -74,9 +89,9 @@ public function testCommentedLoadsVars() public function testLoadsUnreadableFile() { $file = "unreadable.env"; - $path = rtrim($this->fixturesFolder, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file; - chmod($path, 0000); - $this->expectException('InvalidArgumentException'); + $path = rtrim($this->fixturesFolder, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file; + chmod($path, 0000); + $this->expectException('\InvalidArgumentException'); $this->expectExceptionMessage("The .env file is not readable: {$path}"); $dotenv = new DotEnv($this->fixturesFolder, $file); $dotenv->load(); @@ -178,5 +193,4 @@ public function testDotenvAllowsSpecialCharacters() } //-------------------------------------------------------------------- - } diff --git a/tests/system/ControllerTest.php b/tests/system/ControllerTest.php index 208298454bd2..39941b3af868 100644 --- a/tests/system/ControllerTest.php +++ b/tests/system/ControllerTest.php @@ -1,7 +1,9 @@ config = new App(); $this->request = new \CodeIgniter\HTTP\IncomingRequest($this->config, new \CodeIgniter\HTTP\URI('https://somwhere.com'), null, new UserAgent()); $this->response = new \CodeIgniter\HTTP\Response($this->config); + $this->logger = \Config\Services::logger(); $this->codeigniter = new MockCodeIgniter($this->config); } @@ -53,7 +60,8 @@ public function setUp() public function testConstructor() { // make sure we can instantiate one - $this->controller = new Controller($this->request, $this->response); + $this->controller = new Controller(); + $this->controller->initController($this->request, $this->response, $this->logger); $this->assertInstanceOf(Controller::class, $this->controller); } @@ -62,11 +70,12 @@ public function testConstructorHTTPS() $original = $_SERVER; $_SERVER = ['HTTPS' => 'on']; // make sure we can instantiate one - $this->controller = new Class($this->request, $this->response) extends Controller + $this->controller = new Class() extends Controller { - protected $forceHTTPS = 1; }; + $this->controller->initController($this->request, $this->response, $this->logger); + $this->assertInstanceOf(Controller::class, $this->controller); $_SERVER = $original; // restore so code coverage doesn't break } @@ -74,14 +83,18 @@ public function testConstructorHTTPS() //-------------------------------------------------------------------- public function testCachePage() { - $this->controller = new Controller($this->request, $this->response); + $this->controller = new Controller(); + $this->controller->initController($this->request, $this->response, $this->logger); + $this->assertNull($this->controller->cachePage(10)); } public function testValidate() { // make sure we can instantiate one - $this->controller = new Controller($this->request, $this->response); + $this->controller = new Controller(); + $this->controller->initController($this->request, $this->response, $this->logger); + // and that we can attempt validation, with no rules $this->assertFalse($this->controller->validate([])); } @@ -89,11 +102,12 @@ public function testValidate() //-------------------------------------------------------------------- public function testHelpers() { - $this->controller = new Class($this->request, $this->response) extends Controller + $this->controller = new Class() extends Controller { - protected $helpers = ['cookie', 'text']; }; + $this->controller->initController($this->request, $this->response, $this->logger); + $this->assertInstanceOf(Controller::class, $this->controller); } diff --git a/tests/system/Database/Live/ForgeTest.php b/tests/system/Database/Live/ForgeTest.php index 249237654262..55e7112ad00b 100644 --- a/tests/system/Database/Live/ForgeTest.php +++ b/tests/system/Database/Live/ForgeTest.php @@ -39,6 +39,24 @@ public function testCreateTable() $this->assertTrue($exist); } + public function testCreateTableWithAttributes() + { + $this->forge->dropTable('forge_test_attributes', true); + + $this->forge->addField('id'); + + $attributes = [ + 'comment' => "Forge's Test" + ]; + + $this->forge->createTable('forge_test_attributes', false, $attributes); + + $exist = $this->db->tableExists('forge_test_attributes'); + $this->forge->dropTable('forge_test_attributes', true, true); + + $this->assertTrue($exist); + } + public function testAddFields() { diff --git a/tests/system/Database/Live/ModelTest.php b/tests/system/Database/Live/ModelTest.php index a42cd49781e3..baa8387e8484 100644 --- a/tests/system/Database/Live/ModelTest.php +++ b/tests/system/Database/Live/ModelTest.php @@ -54,6 +54,23 @@ public function testFindReturnsMultipleRows() //-------------------------------------------------------------------- + public function testFindActsAsGetWithNoParams() + { + $model = new JobModel($this->db); + + $jobs = $model->asArray()->find(); + + $this->assertCount(4, $jobs); + + $names = array_column($jobs, 'name'); + $this->assertTrue(in_array('Developer', $names)); + $this->assertTrue(in_array('Politician', $names)); + $this->assertTrue(in_array('Accountant', $names)); + $this->assertTrue(in_array('Musician', $names)); + } + + //-------------------------------------------------------------------- + public function testFindRespectsReturnArray() { $model = new JobModel($this->db); @@ -88,49 +105,9 @@ public function testFindRespectsSoftDeletes() $user = $model->withDeleted()->find(4); - $this->assertEquals(1, count($user)); - } - - //-------------------------------------------------------------------- - - public function testFindWhereSimple() - { - $model = new JobModel($this->db); - - $jobs = $model->asObject()->findWhere('id >', 2); - - $this->assertCount(2, $jobs); - $this->assertEquals('Accountant', $jobs[0]->name); - $this->assertEquals('Musician', $jobs[1]->name); - } - - //-------------------------------------------------------------------- - - public function testFindWhereWithArrayWhere() - { - $model = new JobModel($this->db); - - $jobs = $model->asArray()->findWhere(['id' => 1]); - - $this->assertCount(1, $jobs); - $this->assertEquals('Developer', $jobs[0]['name']); - } - - //-------------------------------------------------------------------- - - public function testFindWhereRespectsSoftDeletes() - { - $this->db->table('user')->where('id', 4)->update(['deleted' => 1]); - - $model = new UserModel($this->db); - - $user = $model->findWhere('id >', '2'); - - $this->assertCount(1, $user); - - $user = $model->withDeleted()->findWhere('id >', 2); - - $this->assertCount(2, $user); + // fix for PHP7.2 + $count = is_array($user) ? count($user) : 1; + $this->assertEquals(1, $count); } //-------------------------------------------------------------------- @@ -193,7 +170,9 @@ public function testFirst() $user = $model->where('id >', 2)->first(); - $this->assertEquals(1, count($user)); + // fix for PHP7.2 + $count = is_array($user) ? count($user) : 1; + $this->assertEquals(1, $count); $this->assertEquals(3, $user->id); } @@ -207,7 +186,9 @@ public function testFirstRespectsSoftDeletes() $user = $model->first(); - $this->assertEquals(1, count($user)); + // fix for PHP7.2 + $count = is_array($user) ? count($user) : 1; + $this->assertEquals(1, $count); $this->assertEquals(2, $user->id); $user = $model->withDeleted()->first(); @@ -339,28 +320,32 @@ public function testDeleteWithSoftDeletesPurge() //-------------------------------------------------------------------- - public function testDeleteWhereWithSoftDeletes() + public function testDeleteMultiple() { - $model = new UserModel(); + $model = new JobModel(); - $this->seeInDatabase('user', ['name' =>'Derek Jones', 'deleted' => 0]); - $model->deleteWhere('name', 'Derek Jones'); + $this->seeInDatabase('job', ['name' =>'Developer']); + $this->seeInDatabase('job', ['name' =>'Politician']); - $this->seeInDatabase('user', ['name' => 'Derek Jones', 'deleted' => 1]); + $model->delete([1, 2]); + + $this->dontSeeInDatabase('job', ['name' => 'Developer']); + $this->dontSeeInDatabase('job', ['name' => 'Politician']); + $this->seeInDatabase('job', ['name' =>'Accountant']); } //-------------------------------------------------------------------- - public function testDeleteWhereWithSoftDeletesPurge() + public function testDeleteNoParams() { - $model = new UserModel(); + $model = new JobModel(); - $this->seeInDatabase('user', ['name' =>'Derek Jones', 'deleted' => 0]); + $this->seeInDatabase('job', ['name' =>'Developer']); - $model->deleteWhere('name', 'Derek Jones', true); + $model->where('id', 1)->delete(); - $this->dontSeeInDatabase('user', ['name' => 'Derek Jones']); + $this->dontSeeInDatabase('job', ['name' => 'Developer']); } //-------------------------------------------------------------------- @@ -559,4 +544,172 @@ public function testDeleteEvent() $this->assertTrue($model->hasToken('afterDelete')); } + + public function testSetWorksWithInsert() + { + $model = new EventModel(); + + $this->dontSeeInDatabase('user', [ + 'email' => 'foo@example.com' + ]); + + $model->set([ + 'email' => 'foo@example.com', + 'name' => 'Foo Bar', + 'country' => 'US' + ])->insert(); + + $this->seeInDatabase('user', [ + 'email' => 'foo@example.com' + ]); + } + + public function testSetWorksWithUpdate() + { + $model = new EventModel(); + + $this->dontSeeInDatabase('user', [ + 'email' => 'foo@example.com' + ]); + + $userId = $model->insert([ + 'email' => 'foo@example.com', + 'name' => 'Foo Bar', + 'country' => 'US' + ]); + + $model->set([ + 'name' => 'Fred Flintstone' + ])->update($userId); + + $this->seeInDatabase('user', [ + 'id' => $userId, + 'email' => 'foo@example.com', + 'name' => 'Fred Flintstone' + ]); + } + + public function testSetWorksWithUpdateNoId() + { + $model = new EventModel(); + + $this->dontSeeInDatabase('user', [ + 'email' => 'foo@example.com' + ]); + + $userId = $model->insert([ + 'email' => 'foo@example.com', + 'name' => 'Foo Bar', + 'country' => 'US' + ]); + + $model + ->where('id', $userId) + ->set([ + 'name' => 'Fred Flintstone' + ])->update(); + + $this->seeInDatabase('user', [ + 'id' => $userId, + 'email' => 'foo@example.com', + 'name' => 'Fred Flintstone' + ]); + } + + public function testUpdateArray() + { + $model = new EventModel(); + + $data = [ + 'name' => 'Foo', + 'email' => 'foo@example.com', + 'country' => 'US', + 'deleted' => 0 + ]; + + $id = $model->insert($data); + $model->update([1,2], ['name' => 'Foo Bar']); + + $this->seeInDatabase('user', ['id' => 1, 'name' => 'Foo Bar']); + $this->seeInDatabase('user', ['id' => 2, 'name' => 'Foo Bar']); + } + + public function testInsertBatchSuccess() + { + $job_data = [ + ['name' => 'Comedian', 'description' => 'Theres something in your teeth'], + ['name' => 'Cab Driver', 'description' => 'Iam yellow'], + ]; + + $model = new JobModel($this->db); + $model->insertBatch($job_data); + + $this->seeInDatabase('job', ['name' => 'Comedian']); + $this->seeInDatabase('job', ['name' => 'Cab Driver']); + } + + public function testInsertBatchValidationFail() + { + $job_data = [ + ['name' => 'Comedian', 'description' => null], + ]; + + $model = new JobModel($this->db); + + $this->setPrivateProperty($model, 'validationRules', ['description' => 'required']); + + $this->assertFalse($model->insertBatch($job_data)); + + $error = $model->errors(); + $this->assertTrue(isset($error['description'])); + } + + public function testUpdateBatchSuccess() + { + $data = [ + [ + 'name' => 'Derek Jones', + 'country' => 'Greece' + ], + [ + 'name' => 'Ahmadinejad', + 'country' => 'Greece' + ], + ]; + + $model = new EventModel($this->db); + + $model->updateBatch($data, 'name'); + + $this->seeInDatabase('user', [ + 'name' => 'Derek Jones', + 'country' => 'Greece' + ]); + $this->seeInDatabase('user', [ + 'name' => 'Ahmadinejad', + 'country' => 'Greece' + ]); + } + + //-------------------------------------------------------------------- + + public function testUpdateBatchValidationFail() + { + $data = [ + [ + 'name' => 'Derek Jones', + 'country' => null + ], + ]; + + $model = new EventModel($this->db); + $this->setPrivateProperty($model, 'validationRules', ['country' => 'required']); + + $this->assertFalse($model->updateBatch($data, 'name')); + + $error = $model->errors(); + $this->assertTrue(isset($error['country'])); + } + + //-------------------------------------------------------------------- } diff --git a/tests/system/HTTP/Files/FileCollectionTest.php b/tests/system/HTTP/Files/FileCollectionTest.php index e8960b23e358..26ab526316ea 100644 --- a/tests/system/HTTP/Files/FileCollectionTest.php +++ b/tests/system/HTTP/Files/FileCollectionTest.php @@ -1,9 +1,12 @@ -assertEquals([], $files->all()); } @@ -22,16 +25,16 @@ public function testAllReturnsValidSingleFile() { $_FILES = [ 'userfile' => [ - 'name' => 'someFile.txt', - 'type' => 'text/plain', - 'size' => '124', - 'tmp_name' => '/tmp/myTempFile.txt', - 'error' => 0 + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + 'error' => 0 ] ]; - $collection = new FileCollection(); - $files = $collection->all(); + $collection = new FileCollection(); + $files = $collection->all(); $this->assertCount(1, $files); $file = array_shift($files); @@ -47,16 +50,16 @@ public function testAllReturnsValidMultipleFilesSameName() { $_FILES = [ 'userfile' => [ - 'name' => ['fileA.txt', 'fileB.txt'], - 'type' => ['text/plain', 'text/csv'], - 'size' => ['124', '248'], - 'tmp_name' => ['/tmp/fileA.txt', '/tmp/fileB.txt'], - 'error' => 0 + 'name' => ['fileA.txt', 'fileB.txt'], + 'type' => ['text/plain', 'text/csv'], + 'size' => ['124', '248'], + 'tmp_name' => ['/tmp/fileA.txt', '/tmp/fileB.txt'], + 'error' => 0 ] ]; - $collection = new FileCollection(); - $files = $collection->all(); + $collection = new FileCollection(); + $files = $collection->all(); $this->assertCount(1, $files); $this->assertEquals('userfile', key($files)); @@ -79,24 +82,24 @@ public function testAllReturnsValidMultipleFilesSameName() public function testAllReturnsValidMultipleFilesDifferentName() { $_FILES = [ - 'userfile1' => [ - 'name' => 'fileA.txt', - 'type' => 'text/plain', - 'size' => 124, - 'tmp_name' => '/tmp/fileA.txt', - 'error' => 0 + 'userfile1' => [ + 'name' => 'fileA.txt', + 'type' => 'text/plain', + 'size' => 124, + 'tmp_name' => '/tmp/fileA.txt', + 'error' => 0 ], - 'userfile2' => [ - 'name' => 'fileB.txt', - 'type' => 'text/csv', - 'size' => 248, - 'tmp_name' => '/tmp/fileB.txt', - 'error' => 0 + 'userfile2' => [ + 'name' => 'fileB.txt', + 'type' => 'text/csv', + 'size' => 248, + 'tmp_name' => '/tmp/fileB.txt', + 'error' => 0 ], ]; - $collection = new FileCollection(); - $files = $collection->all(); + $collection = new FileCollection(); + $files = $collection->all(); $this->assertCount(2, $files); $this->assertEquals('userfile1', key($files)); @@ -104,6 +107,7 @@ public function testAllReturnsValidMultipleFilesDifferentName() $this->assertInstanceOf(UploadedFile::class, $file); $this->assertEquals('fileA.txt', $file->getName()); + $this->assertEquals('fileA.txt', $file->getClientName()); $this->assertEquals('/tmp/fileA.txt', $file->getTempName()); $this->assertEquals('txt', $file->getClientExtension()); $this->assertEquals('text/plain', $file->getClientMimeType()); @@ -113,6 +117,7 @@ public function testAllReturnsValidMultipleFilesDifferentName() $this->assertInstanceOf(UploadedFile::class, $file); $this->assertEquals('fileB.txt', $file->getName()); + $this->assertEquals('fileB.txt', $file->getClientName()); $this->assertEquals('/tmp/fileB.txt', $file->getTempName()); $this->assertEquals('txt', $file->getClientExtension()); $this->assertEquals('text/csv', $file->getClientMimeType()); @@ -128,32 +133,32 @@ public function testAllReturnsValidSingleFileNestedName() { $_FILES = [ 'userfile' => [ - 'name' => [ + 'name' => [ 'foo' => [ 'bar' => 'fileA.txt' ] ], - 'type' => [ + 'type' => [ 'foo' => [ 'bar' => 'text/plain' ] ], - 'size' => [ + 'size' => [ 'foo' => [ 'bar' => 124 ] ], - 'tmp_name' => [ + 'tmp_name' => [ 'foo' => [ 'bar' => '/tmp/fileA.txt' ] ], - 'error' => 0 + 'error' => 0 ] ]; - $collection = new FileCollection(); - $files = $collection->all(); + $collection = new FileCollection(); + $files = $collection->all(); $this->assertCount(1, $files); $this->assertEquals('userfile', key($files)); @@ -175,11 +180,11 @@ public function testHasFileWithSingleFile() { $_FILES = [ 'userfile' => [ - 'name' => 'someFile.txt', - 'type' => 'text/plain', - 'size' => '124', - 'tmp_name' => '/tmp/myTempFile.txt', - 'error' => 0 + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + 'error' => 0 ] ]; @@ -194,19 +199,19 @@ public function testHasFileWithSingleFile() public function testHasFileWithMultipleFilesWithDifferentNames() { $_FILES = [ - 'userfile1' => [ - 'name' => 'fileA.txt', - 'type' => 'text/plain', - 'size' => 124, - 'tmp_name' => '/tmp/fileA.txt', - 'error' => 0 + 'userfile1' => [ + 'name' => 'fileA.txt', + 'type' => 'text/plain', + 'size' => 124, + 'tmp_name' => '/tmp/fileA.txt', + 'error' => 0 ], - 'userfile2' => [ - 'name' => 'fileB.txt', - 'type' => 'text/csv', - 'size' => 248, - 'tmp_name' => '/tmp/fileB.txt', - 'error' => 0 + 'userfile2' => [ + 'name' => 'fileB.txt', + 'type' => 'text/csv', + 'size' => 248, + 'tmp_name' => '/tmp/fileB.txt', + 'error' => 0 ], ]; @@ -225,27 +230,27 @@ public function testHasFileWithSingleFileNestedName() { $_FILES = [ 'userfile' => [ - 'name' => [ + 'name' => [ 'foo' => [ 'bar' => 'fileA.txt' ] ], - 'type' => [ + 'type' => [ 'foo' => [ 'bar' => 'text/plain' ] ], - 'size' => [ + 'size' => [ 'foo' => [ 'bar' => 124 ] ], - 'tmp_name' => [ + 'tmp_name' => [ 'foo' => [ 'bar' => '/tmp/fileA.txt' ] ], - 'error' => 0 + 'error' => 0 ] ]; @@ -262,71 +267,168 @@ public function testErrorString() { $_FILES = [ 'userfile' => [ - 'name' => 'someFile.txt', - 'type' => 'text/plain', - 'size' => '124', - 'tmp_name' => '/tmp/myTempFile.txt', - 'error' => UPLOAD_ERR_INI_SIZE + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + 'error' => UPLOAD_ERR_INI_SIZE ] ]; $expected = 'The file "someFile.txt" exceeds your upload_max_filesize ini directive.'; - $collection = new FileCollection(); - $file = $collection->getFile('userfile'); + $collection = new FileCollection(); + $file = $collection->getFile('userfile'); $this->assertEquals($expected, $file->getErrorString()); } - //-------------------------------------------------------------------- - - public function testFileReturnsValidSingleFile() { - $_FILES = [ - 'userfile' => [ - 'name' => 'someFile.txt', - 'type' => 'text/plain', - 'size' => '124', - 'tmp_name' => '/tmp/myTempFile.txt', - 'error' => 0 - ] - ]; - - $collection = new FileCollection(); - $file = $collection->getFile('userfile'); - $this->assertInstanceOf(UploadedFile::class, $file); - - $this->assertEquals('someFile.txt', $file->getName()); - $this->assertEquals(124, $file->getSize()); - } - - //-------------------------------------------------------------------- - - public function testFileNoExistSingleFile() { - $_FILES = [ - 'userfile' => [ - 'name' => 'someFile.txt', - 'type' => 'text/plain', - 'size' => '124', - 'tmp_name' => '/tmp/myTempFile.txt', - 'error' => 0 - ] - ]; - - $collection = new FileCollection(); - $file = $collection->getFile('fileuser'); - $this->AssertNull($file); - } - - //-------------------------------------------------------------------- - - public function testFileReturnValidMultipleFiles() { - $_FILES = [ + public function testErrorStringWithUnknownError() + { + $_FILES = [ + 'userfile' => [ + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + 'error' => 123 + ] + ]; + + $expected = 'The file "someFile.txt" was not uploaded due to an unknown error.'; + + $collection = new FileCollection(); + $file = $collection->getFile('userfile'); + + $this->assertEquals($expected, $file->getErrorString()); + } + + public function testErrorStringWithNoError() + { + $_FILES = [ + 'userfile' => [ + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + ] + ]; + + $expected = 'The file uploaded with success.'; + + $collection = new FileCollection(); + $file = $collection->getFile('userfile'); + + $this->assertEquals($expected, $file->getErrorString()); + } + + //-------------------------------------------------------------------- + + public function testError() + { + $_FILES = [ + 'userfile' => [ + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + 'error' => UPLOAD_ERR_INI_SIZE + ] + ]; + + $collection = new FileCollection(); + $file = $collection->getFile('userfile'); + + $this->assertEquals(UPLOAD_ERR_INI_SIZE, $file->getError()); + } + + public function testErrorWithUnknownError() + { + $_FILES = [ 'userfile' => [ - 'name' => ['fileA.txt', 'fileB.txt'], - 'type' => ['text/plain', 'text/csv'], - 'size' => ['124', '248'], - 'tmp_name' => ['/tmp/fileA.txt', '/tmp/fileB.txt'], - 'error' => 0 + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + ] + ]; + + $collection = new FileCollection(); + $file = $collection->getFile('userfile'); + + $this->assertEquals(0, $file->getError()); + } + + public function testErrorWithNoError() + { + $_FILES = [ + 'userfile' => [ + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + 'error' => 0 + ] + ]; + + $collection = new FileCollection(); + $file = $collection->getFile('userfile'); + + $this->assertEquals(UPLOAD_ERR_OK, $file->getError()); + } + + //-------------------------------------------------------------------- + + public function testFileReturnsValidSingleFile() + { + $_FILES = [ + 'userfile' => [ + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + 'error' => 0 + ] + ]; + + $collection = new FileCollection(); + $file = $collection->getFile('userfile'); + $this->assertInstanceOf(UploadedFile::class, $file); + + $this->assertEquals('someFile.txt', $file->getName()); + $this->assertEquals(124, $file->getSize()); + } + + //-------------------------------------------------------------------- + + public function testFileNoExistSingleFile() + { + $_FILES = [ + 'userfile' => [ + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + 'error' => 0 + ] + ]; + + $collection = new FileCollection(); + $file = $collection->getFile('fileuser'); + $this->AssertNull($file); + } + + //-------------------------------------------------------------------- + + public function testFileReturnValidMultipleFiles() + { + $_FILES = [ + 'userfile' => [ + 'name' => ['fileA.txt', 'fileB.txt'], + 'type' => ['text/plain', 'text/csv'], + 'size' => ['124', '248'], + 'tmp_name' => ['/tmp/fileA.txt', '/tmp/fileB.txt'], + 'error' => 0 ] ]; @@ -347,195 +449,100 @@ public function testFileReturnValidMultipleFiles() { $this->assertEquals('txt', $file_2->getClientExtension()); $this->assertEquals('text/csv', $file_2->getClientMimeType()); $this->assertEquals(248, $file_2->getSize()); - } + } - //-------------------------------------------------------------------- + //-------------------------------------------------------------------- - public function testFileWithMultipleFilesNestedName() { - $_FILES = [ + public function testFileWithMultipleFilesNestedName() + { + $_FILES = [ 'my-form' => [ - 'name' => [ + 'name' => [ 'details' => [ - 'avatars' => ['fileA.txt','fileB.txt'] + 'avatars' => ['fileA.txt', 'fileB.txt'] ] ], - 'type' => [ + 'type' => [ 'details' => [ - 'avatars' => ['text/plain','text/plain'] + 'avatars' => ['text/plain', 'text/plain'] ] ], - 'size' => [ + 'size' => [ 'details' => [ - 'avatars' => [125,243] + 'avatars' => [125, 243] ] ], - 'tmp_name' => [ + 'tmp_name' => [ 'details' => [ - 'avatars' => ['/tmp/fileA.txt','/tmp/fileB.txt'] + 'avatars' => ['/tmp/fileA.txt', '/tmp/fileB.txt'] ] ], - 'error' => [ + 'error' => [ 'details' => [ - 'avatars' => [0,0] + 'avatars' => [0, 0] ] ], ] ]; - $collection = new FileCollection(); + $collection = new FileCollection(); - $file_1 = $collection->getFile('my-form.details.avatars.0'); - $this->assertInstanceOf(UploadedFile::class, $file_1); - $this->assertEquals('fileA.txt', $file_1->getName()); + $file_1 = $collection->getFile('my-form.details.avatars.0'); + $this->assertInstanceOf(UploadedFile::class, $file_1); + $this->assertEquals('fileA.txt', $file_1->getName()); $this->assertEquals('/tmp/fileA.txt', $file_1->getTempName()); $this->assertEquals('txt', $file_1->getClientExtension()); $this->assertEquals('text/plain', $file_1->getClientMimeType()); $this->assertEquals(125, $file_1->getSize()); - $file_2 = $collection->getFile('my-form.details.avatars.1'); - $this->assertInstanceOf(UploadedFile::class, $file_2); - $this->assertEquals('fileB.txt', $file_2->getName()); + $file_2 = $collection->getFile('my-form.details.avatars.1'); + $this->assertInstanceOf(UploadedFile::class, $file_2); + $this->assertEquals('fileB.txt', $file_2->getName()); $this->assertEquals('/tmp/fileB.txt', $file_2->getTempName()); $this->assertEquals('txt', $file_2->getClientExtension()); $this->assertEquals('text/plain', $file_2->getClientMimeType()); $this->assertEquals(243, $file_2->getSize()); - } - - /** - * @group move-file - */ - public function testMoveWhereOverwriteIsFalseWithMultipleFilesWithSameName() - { - $finalFilename = 'fileA'; - - $_FILES = [ - 'userfile1' => [ - 'name' => $finalFilename . '.txt', - 'type' => 'text/plain', - 'size' => 124, - 'tmp_name' => '/tmp/fileA.txt', - 'error' => 0 - ], - 'userfile2' => [ - 'name' => 'fileA.txt', - 'type' => 'text/csv', - 'size' => 248, - 'tmp_name' => '/tmp/fileB.txt', - 'error' => 0 - ], - ]; - - $collection = new FileCollection(); - - $this->assertTrue($collection->hasFile('userfile1')); - $this->assertTrue($collection->hasFile('userfile2')); - - $destination = '/tmp/destination/'; - - // Create the destination if not exists - is_dir($destination) || mkdir($destination, 0777, true); - - foreach ($collection->all() as $file) { - $this->assertInstanceOf(UploadedFile::class, $file); - $file->move($destination, $file->getName(), false); - } - - $this->assertFileExists($destination . $finalFilename . '.txt'); - $this->assertFileExists($destination . $finalFilename . '_1.txt'); - - // Delete the recently created files for the destination above - foreach(glob($destination . "*") as $f) { - unlink($f); - } - // Delete the recently created destination dir - rmdir($destination); - } - - //-------------------------------------------------------------------- - - /** - * @group move-file - */ - public function testMoveWhereOverwriteIsTrueWithMultipleFilesWithSameName() - { - $finalFilename = 'file_with_delimiters_underscore'; - - $_FILES = [ - 'userfile1' => [ - 'name' => $finalFilename . '.txt', - 'type' => 'text/plain', - 'size' => 124, - 'tmp_name' => '/tmp/fileA.txt', - 'error' => 0 - ], - 'userfile2' => [ - 'name' => $finalFilename . '.txt', - 'type' => 'text/csv', - 'size' => 248, - 'tmp_name' => '/tmp/fileB.txt', - 'error' => 0 - ], - 'userfile3' => [ - 'name' => $finalFilename . '.txt', - 'type' => 'text/csv', - 'size' => 248, - 'tmp_name' => '/tmp/fileC.txt', - 'error' => 0 - ], - ]; - - $collection = new FileCollection(); - - $this->assertTrue($collection->hasFile('userfile1')); - $this->assertTrue($collection->hasFile('userfile2')); - $this->assertTrue($collection->hasFile('userfile3')); - - $destination = '/tmp/destination/'; - - // Create the destination if not exists - is_dir($destination) || mkdir($destination, 0777, true); - - foreach ($collection->all() as $file) { - $this->assertInstanceOf(UploadedFile::class, $file); - $file->move($destination, $file->getName(), true); - } - - $this->assertFileExists($destination . $finalFilename . '.txt'); - $this->assertFileNotExists($destination . $finalFilename . '_1.txt'); - $this->assertFileNotExists($destination . $finalFilename . '_2.txt'); - - // Delete the recently created files for the destination above - foreach(glob($destination . "*") as $f) { - unlink($f); - } - // Delete the recently created destination dir - rmdir($destination); - } + } //-------------------------------------------------------------------- -} + + public function testDoesntHaveFile() + { + $_FILES = [ + 'my-form' => [ + 'name' => [ + 'details' => [ + 'avatars' => ['fileA.txt', 'fileB.txt'] + ] + ], + 'type' => [ + 'details' => [ + 'avatars' => ['text/plain', 'text/plain'] + ] + ], + 'size' => [ + 'details' => [ + 'avatars' => [125, 243] + ] + ], + 'tmp_name' => [ + 'details' => [ + 'avatars' => ['/tmp/fileA.txt', '/tmp/fileB.txt'] + ] + ], + 'error' => [ + 'details' => [ + 'avatars' => [0, 0] + ] + ], + ] + ]; -/* - * Overwrite the function so that it will only check whether the file exists or not. - * Original function also checks if the file was uploaded with a POST request. - * - * This overwrite is for testing the move operation. - */ -function is_uploaded_file($filename) -{ - if (! file_exists($filename)) - { - file_put_contents($filename, 'data'); - } - return file_exists($filename); -} + $collection = new FileCollection(); -/* - * Overwrite the function so that it just copy without checking the file is an uploaded file. - * - * This overwrite is for testing the move operation. - */ -function move_uploaded_file($filename, $destination) -{ - return copy($filename, $destination); + $this->assertFalse($collection->hasFile('my-form.detailz.avatars.0')); + $this->assertNull($collection->getFile('my-form.detailz.avatars.0')); + } + + //-------------------------------------------------------------------- } diff --git a/tests/system/HTTP/Files/FileMovingTest.php b/tests/system/HTTP/Files/FileMovingTest.php new file mode 100644 index 000000000000..be9cb89dcfa9 --- /dev/null +++ b/tests/system/HTTP/Files/FileMovingTest.php @@ -0,0 +1,272 @@ +root = vfsStream::setup(); + $this->path = '_support/Files/'; + vfsStream::copyFromFileSystem(TESTPATH . $this->path, $this->root); + $this->start = $this->root->url() . '/'; + + $_FILES = []; + } + + public function tearDown() + { + parent::tearDown(); + + $this->root = null; + if (is_dir('/tmp/destination')) + rmdir('/tmp/destination'); + } + + //-------------------------------------------------------------------- + + public function testMove() + { + $finalFilename = 'fileA'; + + $_FILES = [ + 'userfile1' => [ + 'name' => $finalFilename . '.txt', + 'type' => 'text/plain', + 'size' => 124, + 'tmp_name' => '/tmp/fileA.txt', + 'error' => 0 + ], + 'userfile2' => [ + 'name' => 'fileA.txt', + 'type' => 'text/csv', + 'size' => 248, + 'tmp_name' => '/tmp/fileB.txt', + 'error' => 0 + ], + ]; + + $collection = new FileCollection(); + + $this->assertTrue($collection->hasFile('userfile1')); + $this->assertTrue($collection->hasFile('userfile2')); + + $destination = $this->root->url() . '/destination'; + + // Create the destination if not exists + is_dir($destination) || mkdir($destination, 0777, true); + + foreach ($collection->all() as $file) + { + $this->assertInstanceOf(UploadedFile::class, $file); + $file->move($destination, $file->getName(), false); + } + + $this->assertTrue($this->root->hasChild('destination/' . $finalFilename . '.txt')); + $this->assertTrue($this->root->hasChild('destination/' . $finalFilename . '_1.txt')); + } + + //-------------------------------------------------------------------- + + public function testMoveOverwriting() + { + $finalFilename = 'file_with_delimiters_underscore'; + + $_FILES = [ + 'userfile1' => [ + 'name' => $finalFilename . '.txt', + 'type' => 'text/plain', + 'size' => 124, + 'tmp_name' => '/tmp/fileA.txt', + 'error' => 0 + ], + 'userfile2' => [ + 'name' => $finalFilename . '.txt', + 'type' => 'text/csv', + 'size' => 248, + 'tmp_name' => '/tmp/fileB.txt', + 'error' => 0 + ], + 'userfile3' => [ + 'name' => $finalFilename . '.txt', + 'type' => 'text/csv', + 'size' => 248, + 'tmp_name' => '/tmp/fileC.txt', + 'error' => 0 + ], + ]; + + $collection = new FileCollection(); + + $this->assertTrue($collection->hasFile('userfile1')); + $this->assertTrue($collection->hasFile('userfile2')); + $this->assertTrue($collection->hasFile('userfile3')); + + $destination = $this->root->url() . '/destination'; + + // Create the destination if not exists + is_dir($destination) || mkdir($destination, 0777, true); + + foreach ($collection->all() as $file) + { + $this->assertInstanceOf(UploadedFile::class, $file); + $file->move($destination, $file->getName(), true); + } + + $this->assertTrue($this->root->hasChild('destination/' . $finalFilename . '.txt')); + $this->assertFalse($this->root->hasChild('destination/' . $finalFilename . '_1.txt')); + $this->assertFalse($this->root->hasChild('destination/' . $finalFilename . '_2.txt')); + $this->assertFileExists($destination . '/' . $finalFilename . '.txt'); + } + + //-------------------------------------------------------------------- + + public function testMoved() + { + $finalFilename = 'fileA'; + + $_FILES = [ + 'userfile1' => [ + 'name' => $finalFilename . '.txt', + 'type' => 'text/plain', + 'size' => 124, + 'tmp_name' => '/tmp/fileA.txt', + 'error' => 0 + ], + ]; + + $collection = new FileCollection(); + + $this->assertTrue($collection->hasFile('userfile1')); + + $destination = $this->root->url() . '/destination'; + + // Create the destination if not exists + is_dir($destination) || mkdir($destination, 0777, true); + + $file = $collection->getFile('userfile1'); + + $this->assertInstanceOf(UploadedFile::class, $file); + $this->assertFalse($file->hasMoved()); + $file->move($destination, $file->getName(), false); + $this->assertTrue($file->hasMoved()); + } + + //-------------------------------------------------------------------- + + public function testAlreadyMoved() + { + $finalFilename = 'fileA'; + + $_FILES = [ + 'userfile1' => [ + 'name' => $finalFilename . '.txt', + 'type' => 'text/plain', + 'size' => 124, + 'tmp_name' => '/tmp/fileA.txt', + 'error' => 0 + ], + ]; + + $collection = new FileCollection(); + + $this->assertTrue($collection->hasFile('userfile1')); + + $destination = $this->root->url() . '/destination'; + + // Create the destination if not exists + is_dir($destination) || mkdir($destination, 0777, true); + + $this->expectException(HTTPException::class); + + foreach ($collection->all() as $file) + { + $file->move($destination, $file->getName(), false); + $file->move($destination, $file->getName(), false); + } + } + + //-------------------------------------------------------------------- + + public function testInvalidFile() + { + $_FILES = [ + 'userfile' => [ + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + 'error' => UPLOAD_ERR_INI_SIZE + ] + ]; + + $destination = $this->root->url() . '/destination'; + // don't create the folder, so setPath() is invoked. + + $collection = new FileCollection(); + $file = $collection->getFile('userfile'); + + $this->expectException(HTTPException::class); + $file->move($destination, $file->getName(), false); + } + + //-------------------------------------------------------------------- + + public function testFailedMove() + { + $_FILES = [ + 'userfile' => [ + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + 'error' => 0, + ] + ]; + + $destination = '/tmp/destination'; + // Create the destination and make it read only + is_dir($destination) || mkdir($destination, 0400, true); + + $collection = new FileCollection(); + $file = $collection->getFile('userfile'); + + $this->expectException(HTTPException::class); + $file->move($destination, $file->getName(), false); + } + + //-------------------------------------------------------------------- +} + +/* + * Overwrite the function so that it will only check whether the file exists or not. + * Original function also checks if the file was uploaded with a POST request. + * + * This overwrite is for testing the move operation. + */ + +function is_uploaded_file($filename) +{ + if ( ! file_exists($filename)) + { + file_put_contents($filename, 'data'); + } + return file_exists($filename); +} + +/* + * Overwrite the function so that it just copy without checking the file is an uploaded file. + * + * This overwrite is for testing the move operation. + */ + +function move_uploaded_file($filename, $destination) +{ + return copy($filename, $destination); +} diff --git a/tests/system/HTTP/IncomingRequestDetectingTest.php b/tests/system/HTTP/IncomingRequestDetectingTest.php new file mode 100644 index 000000000000..0872f7462a73 --- /dev/null +++ b/tests/system/HTTP/IncomingRequestDetectingTest.php @@ -0,0 +1,143 @@ +request = new IncomingRequest(new App(), new URI($origin), null, new UserAgent()); + } + + //-------------------------------------------------------------------- + + public function testPathDefault() + { + $this->request->uri = '/index.php/woot?code=good#pos'; + $_SERVER['REQUEST_URI'] = '/index.php/woot'; + $_SERVER['SCRIPT_NAME'] = '/index.php'; + $expected = 'woot'; + $this->assertEquals($expected, $this->request->detectPath()); + } + + public function testPathRequestURI() + { + $this->request->uri = '/index.php/woot?code=good#pos'; + $_SERVER['REQUEST_URI'] = '/index.php/woot'; + $_SERVER['SCRIPT_NAME'] = '/index.php'; + $expected = 'woot'; + $this->assertEquals($expected, $this->request->detectPath('REQUEST_URI')); + } + + public function testPathRequestURINested() + { + $this->request->uri = '/ci/index.php/woot?code=good#pos'; + $_SERVER['REQUEST_URI'] = '/index.php/woot'; + $_SERVER['SCRIPT_NAME'] = '/index.php'; + $expected = 'woot'; + $this->assertEquals($expected, $this->request->detectPath('REQUEST_URI')); + } + + public function testPathRequestURISubfolder() + { + $this->request->uri = '/ci/index.php/popcorn/woot?code=good#pos'; + $_SERVER['REQUEST_URI'] = '/ci/index.php/popcorn/woot'; + $_SERVER['SCRIPT_NAME'] = '/index.php'; + $expected = 'popcorn/woot'; + $this->assertEquals($expected, $this->request->detectPath('REQUEST_URI')); + } + + public function testPathRequestURINginx() + { + $this->request->uri = '/ci/index.php/woot?code=good#pos'; + $_SERVER['REQUEST_URI'] = '/index.php/woot?code=good'; + $_SERVER['SCRIPT_NAME'] = '/index.php'; + $expected = 'woot'; + $this->assertEquals($expected, $this->request->detectPath('REQUEST_URI')); + } + + public function testPathRequestURINginxRedirecting() + { + $this->request->uri = '/?/ci/index.php/woot'; + $_SERVER['REQUEST_URI'] = '/?/ci/woot'; + $_SERVER['SCRIPT_NAME'] = '/index.php'; + $expected = 'ci/woot'; + $this->assertEquals($expected, $this->request->detectPath('REQUEST_URI')); + } + + public function testPathRequestURISuppressed() + { + $this->request->uri = '/woot?code=good#pos'; + $_SERVER['REQUEST_URI'] = '/woot'; + $_SERVER['SCRIPT_NAME'] = '/'; + $expected = 'woot'; + $this->assertEquals($expected, $this->request->detectPath('REQUEST_URI')); + } + + //-------------------------------------------------------------------- + + public function testPathQueryString() + { + $this->request->uri = '/?/ci/index.php/woot'; + $_SERVER['REQUEST_URI'] = '/?/ci/woot'; + $_SERVER['QUERY_STRING'] = '/ci/woot'; + $_SERVER['SCRIPT_NAME'] = '/index.php'; + $expected = 'ci/woot'; + $this->assertEquals($expected, $this->request->detectPath('QUERY_STRING')); + } + + public function testPathQueryStringEmpty() + { + $this->request->uri = '/?/ci/index.php/woot'; + $_SERVER['REQUEST_URI'] = '/?/ci/woot'; + $_SERVER['QUERY_STRING'] = ''; + $_SERVER['SCRIPT_NAME'] = '/index.php'; + $expected = ''; + $this->assertEquals($expected, $this->request->detectPath('QUERY_STRING')); + } + + //-------------------------------------------------------------------- + + public function testPathPathInfo() + { + $this->request->uri = '/index.php/woot?code=good#pos'; + $this->request->setGlobal('server', [ + 'PATH_INFO' => null, + ]); + $_SERVER['REQUEST_URI'] = '/index.php/woot'; + $_SERVER['SCRIPT_NAME'] = '/index.php'; + $expected = 'woot'; + $this->assertEquals($expected, $this->request->detectPath('PATH_INFO')); + } + + public function testPathPathInfoGlobal() + { + $this->request->uri = '/index.php/woot?code=good#pos'; + $this->request->uri = '/index.php/woot?code=good#pos'; + $this->request->setGlobal('server', [ + 'PATH_INFO' => 'silliness', + ]); + $_SERVER['REQUEST_URI'] = '/index.php/woot'; + $_SERVER['SCRIPT_NAME'] = '/index.php'; + + $expected = 'silliness'; + $this->assertEquals($expected, $this->request->detectPath('PATH_INFO')); + } + +} diff --git a/tests/system/HTTP/IncomingRequestTest.php b/tests/system/HTTP/IncomingRequestTest.php index eb8245510574..36cf7425a1b8 100644 --- a/tests/system/HTTP/IncomingRequestTest.php +++ b/tests/system/HTTP/IncomingRequestTest.php @@ -1,12 +1,16 @@ -assertNull($this->request->getVar('TESTY')); } - //-------------------------------------------------------------------- - public function testCanGrabGetVars() { $_GET['TEST'] = 5; @@ -41,8 +43,6 @@ public function testCanGrabGetVars() $this->assertNull($this->request->getGEt('TESTY')); } - //-------------------------------------------------------------------- - public function testCanGrabPostVars() { $_POST['TEST'] = 5; @@ -51,8 +51,6 @@ public function testCanGrabPostVars() $this->assertNull($this->request->getPost('TESTY')); } - //-------------------------------------------------------------------- - public function testCanGrabPostBeforeGet() { $_POST['TEST'] = 5; @@ -64,20 +62,29 @@ public function testCanGrabPostBeforeGet() //-------------------------------------------------------------------- - /** - * @group single - */ - public function testCanGetOldInput() - { - $_SESSION['_ci_old_input'] = [ - 'get' => ['one' => 'two'], - 'post' => ['name' => 'foo'] - ]; + public function testCanGetOldInput() + { + $_SESSION['_ci_old_input'] = [ + 'get' => ['one' => 'two'], + 'post' => ['name' => 'foo'] + ]; - $this->assertEquals('foo', $this->request->getOldInput('name')); - $this->assertEquals('two', $this->request->getOldInput('one')); - } + $this->assertEquals('foo', $this->request->getOldInput('name')); + $this->assertEquals('two', $this->request->getOldInput('one')); + } + public function testCanGetOldInputDotted() + { + $_SESSION['_ci_old_input'] = [ + 'get' => ['apple' => ['name' => 'two']], + 'post' => ['banana' => ['name' => 'foo']], + ]; + + $this->assertEquals('foo', $this->request->getOldInput('banana.name')); + $this->assertEquals('two', $this->request->getOldInput('apple.name')); + } + + //-------------------------------------------------------------------- public function testCanGrabServerVars() { @@ -89,8 +96,6 @@ public function testCanGrabServerVars() $this->assertNull($this->request->getServer('TESTY')); } - //-------------------------------------------------------------------- - public function testCanGrabEnvVars() { $server = $this->getPrivateProperty($this->request, 'globals'); @@ -101,8 +106,6 @@ public function testCanGrabEnvVars() $this->assertNull($this->request->getEnv('TESTY')); } - //-------------------------------------------------------------------- - public function testCanGrabCookieVars() { $_COOKIE['TEST'] = 5; @@ -111,7 +114,7 @@ public function testCanGrabCookieVars() $this->assertNull($this->request->getCookie('TESTY')); } - //-------------------------------------------------------------------- + //-------------------------------------------------------------------- public function testStoresDefaultLocale() { @@ -121,8 +124,6 @@ public function testStoresDefaultLocale() $this->assertEquals($config->defaultLocale, $this->request->getLocale()); } - //-------------------------------------------------------------------- - public function testSetLocaleSaves() { $config = new App(); @@ -136,6 +137,19 @@ public function testSetLocaleSaves() $this->assertEquals('en', $request->getLocale()); } + public function testSetBadLocale() + { + $config = new App(); + $config->supportedLocales = ['en', 'es']; + $config->defaultLocale = 'es'; + $config->baseURL = 'http://example.com'; + + $request = new IncomingRequest($config, new URI(), null, new UserAgent()); + + $request->setLocale('xx'); + $this->assertEquals('es', $request->getLocale()); + } + //-------------------------------------------------------------------- public function testNegotiatesLocale() @@ -153,6 +167,44 @@ public function testNegotiatesLocale() $this->assertEquals('es', $request->getLocale()); } + // The negotiation tests below are not intended to exercise the HTTP\Negotiate class - + // that is up to the NegotiateTest. These are only to make sure that the requests + // flow through to the negotiator + + public function testNegotiatesNot() + { + $this->request->setHeader('Accept-Charset', 'iso-8859-5, unicode-1-1;q=0.8'); + + $this->expectException(Exceptions\HTTPException::class); + $this->request->negotiate('something bogus', ['iso-8859-5', 'unicode-1-1']); + } + + public function testNegotiatesCharset() + { +// $_SERVER['HTTP_ACCEPT_CHARSET'] = 'iso-8859-5, unicode-1-1;q=0.8'; + $this->request->setHeader('Accept-Charset', 'iso-8859-5, unicode-1-1;q=0.8'); + + $this->assertEquals(strtolower($this->request->config->charset), $this->request->negotiate('charset', ['iso-8859', 'unicode-1-2'])); + } + + public function testNegotiatesMedia() + { + $this->request->setHeader('Accept', 'text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c'); + $this->assertEquals('text/html', $this->request->negotiate('media', ['text/html', 'text/x-c', 'text/x-dvi', 'text/plain'])); + } + + public function testNegotiatesEncoding() + { + $this->request->setHeader('Accept-Encoding', 'gzip;q=1.0, identity; q=0.4, compress;q=0.5'); + $this->assertEquals('gzip', $this->request->negotiate('encoding', ['gzip', 'compress'])); + } + + public function testNegotiatesLanguage() + { + $this->request->setHeader('Accept-Language', 'da, en-gb;q=0.8, en;q=0.7'); + $this->assertEquals('en', $this->request->negotiate('language', ['en', 'da'])); + } + //-------------------------------------------------------------------- public function testCanGrabGetRawJSON() @@ -172,8 +224,6 @@ public function testCanGrabGetRawJSON() $this->assertEquals($expected, $request->getJSON(true)); } - //-------------------------------------------------------------------- - public function testCanGrabGetRawInput() { $rawstring = 'username=admin001&role=administrator&usepass=0'; @@ -193,4 +243,73 @@ public function testCanGrabGetRawInput() } //-------------------------------------------------------------------- + + public function testIsCLI() + { + // this should be the case in unit testing + $this->assertTrue($this->request->isCLI()); + } + + public function testIsAJAX() + { + $_SERVER['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest'; + $this->assertTrue($this->request->isAJAX()); + } + + //-------------------------------------------------------------------- + + public function testIsSecure() + { + $_SERVER['HTTPS'] = 'on'; + $this->assertTrue($this->request->isSecure()); + } + + public function testIsSecureFrontEnd() + { + $_SERVER['HTTP_FRONT_END_HTTPS'] = 'on'; + $this->assertTrue($this->request->isSecure()); + } + + public function testIsSecureForwarded() + { + $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'https'; + $this->assertTrue($this->request->isSecure()); + } + + //-------------------------------------------------------------------- + + public function testUserAgent() + { + $_SERVER['HTTP_USER_AGENT'] = 'Mozilla'; + $config = new App(); + $request = new IncomingRequest($config, new URI(), null, new UserAgent()); + $this->assertEquals('Mozilla', $request->getUserAgent()); + } + + //-------------------------------------------------------------------- + + public function testFileCollectionFactory() + { + $_FILES = [ + 'userfile' => [ + 'name' => 'someFile.txt', + 'type' => 'text/plain', + 'size' => '124', + 'tmp_name' => '/tmp/myTempFile.txt', + 'error' => 0 + ] + ]; + + $files = $this->request->getFiles(); + $this->assertCount(1, $files); + + $file = array_shift($files); + $this->assertInstanceOf(UploadedFile::class, $file); + + $this->assertEquals('someFile.txt', $file->getName()); + $this->assertEquals(124, $file->getSize()); + } + + //-------------------------------------------------------------------- + } diff --git a/tests/system/HTTP/MessageTest.php b/tests/system/HTTP/MessageTest.php index 339568c4ca75..d7c5f8c861d3 100644 --- a/tests/system/HTTP/MessageTest.php +++ b/tests/system/HTTP/MessageTest.php @@ -221,7 +221,7 @@ public function testPopulateHeadersWithoutContentType() $_SERVER = $original; // restore so code coverage doesn't break } - //-------------------------------------------------------------------- + //-------------------------------------------------------------------- public function testPopulateHeadersWithoutHTTP() { diff --git a/tests/system/HTTP/NegotiateTest.php b/tests/system/HTTP/NegotiateTest.php index 75bcd67a940c..4dbeefc39c14 100644 --- a/tests/system/HTTP/NegotiateTest.php +++ b/tests/system/HTTP/NegotiateTest.php @@ -1,9 +1,12 @@ -request->setHeader('Accept', 'text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c'); + $this->negotiate->setRequest($this->request); $this->assertEquals('text/html', $this->negotiate->media(['text/html', 'text/x-c', 'text/x-dvi', 'text/plain'])); $this->assertEquals('text/x-c', $this->negotiate->media(['text/x-c', 'text/x-dvi', 'text/plain'])); @@ -50,7 +54,7 @@ public function testNegotiateMediaFindsHighestMatch() public function testParseHeaderDeterminesCorrectPrecedence() { - $header =$this->negotiate->parseHeader('text/*, text/plain, text/plain;format=flowed, */*'); + $header = $this->negotiate->parseHeader('text/*, text/plain, text/plain;format=flowed, */*'); $this->assertEquals('text/plain', $header[0]['value']); $this->assertEquals('flowed', $header[0]['params']['format']); @@ -134,4 +138,36 @@ public function testAcceptLanguageBasics() } //-------------------------------------------------------------------- + public function testBestMatchEmpty() + { + $this->expectException(Exceptions\HTTPException::class); + $this->negotiate->media([]); + } + + public function testBestMatchNoHeader() + { + $this->request->setHeader('Accept',''); + $this->assertEquals('', $this->negotiate->media(['apple', 'banana'], true)); + $this->assertEquals('apple/mac', $this->negotiate->media(['apple/mac', 'banana/yellow'], false)); + } + + public function testBestMatchNotAcceptable() + { + $this->request->setHeader('Accept','popcorn/cheddar'); + $this->assertEquals('apple/mac', $this->negotiate->media(['apple/mac', 'banana/yellow'],false)); + } + + public function testBestMatchFirstSupported() + { + $this->request->setHeader('Accept','popcorn/cheddar, */*'); + $this->assertEquals('apple/mac', $this->negotiate->media(['apple/mac', 'banana/yellow'],false)); + } + + public function testBestMatchLowQuality() + { + $this->request->setHeader('Accept','popcorn/cheddar;q=0, apple/mac, */*'); + $this->assertEquals('apple/mac', $this->negotiate->media(['apple/mac', 'popcorn/cheddar'],false)); + $this->assertEquals('apple/mac', $this->negotiate->media(['popcorn/cheddar','apple/mac'],false)); + } + } diff --git a/tests/system/HTTP/RequestTest.php b/tests/system/HTTP/RequestTest.php index 8d189dbb2ab7..dce721fb1bcc 100644 --- a/tests/system/HTTP/RequestTest.php +++ b/tests/system/HTTP/RequestTest.php @@ -1,4 +1,5 @@ -request = new Request(new App()); - $_POST = []; - $_GET = []; + $this->request = new Request(new App()); + $_POST = []; + $_GET = []; } + //-------------------------------------------------------------------- + public function testFetchGlobalsSingleValue() { - $_POST['foo'] = 'bar'; - $_GET['bar'] = 'baz'; + $_POST['foo'] = 'bar'; + $_GET['bar'] = 'baz'; $this->assertEquals('bar', $this->request->fetchGlobal('post', 'foo')); $this->assertEquals('baz', $this->request->fetchGlobal('get', 'bar')); @@ -38,8 +42,8 @@ public function testFetchGlobalsReturnsNullWhenNotFound() public function testFetchGlobalsFiltersValues() { $this->request->setGlobal('post', [ - 'foo' => 'bar', - script_tag('http://site.com/js/mystyles.js') - ); + $target = 'http://site.com/js/mystyles.js'; + $expected = ''; + $this->assertEquals($expected, script_tag($target)); + } + + public function testScriptTagWithoutProtocol() + { + $target = 'js/mystyles.js'; + $expected = ''; + $this->assertEquals($expected, script_tag($target)); + } + + public function testScriptTagWithIndexpage() + { + $target = 'js/mystyles.js'; + $expected = ''; + $this->assertEquals($expected, script_tag($target, true)); } // ------------------------------------------------------------------------ public function testLinkTag() { - $this->assertEquals - ( - '', - link_tag('http://site.com/css/mystyles.css') - ); + $target = 'css/mystyles.css'; + $expected = ''; + $this->assertEquals($expected, link_tag($target)); + } + + public function testLinkTagComplete() + { + $target = 'https://styles.com/css/mystyles.css'; + $expected = ''; + $this->assertEquals($expected, link_tag($target, 'banana', 'fruit', 'Go away', 'VHS')); + } + + public function testLinkTagArray() + { + $parms = [ + 'href' => 'css/mystyles.css', + 'indexPage' => true, + ]; + $expected = ''; + $this->assertEquals($expected, link_tag($parms)); } // ------------------------------------------------------------------------ public function testDocType() { - $this->assertEquals - ( - '', - doctype('html4-strict') - ); + $target = 'html4-strict'; + $expected = ''; + $this->assertEquals($expected, doctype($target)); + } + + public function testDocTypeDefault() + { + $expected = ''; + $this->assertEquals($expected, doctype()); + } + + public function testDocTypeInvalid() + { + $target = 'good-guess'; + $this->assertEquals(false, doctype($target)); } // ------------------------------------------------------------------------ @@ -182,23 +229,39 @@ public function testDocType() public function testVideo() { $expected = << + EOH; - $video = video - ( - 'test.mp4', - 'Your browser does not support the video tag.', - 'controls' - ); + $target = 'http://www.codeigniter.com/test.mp4'; + $message = 'Your browser does not support the video tag.'; + $video = video($target, $message, 'controls'); + $this->assertEquals($expected, $video); + } + public function testVideoWithTracks() + { + $expected = << + + + Your browser does not support the video tag. + + +EOH; + + $target = 'test.mp4'; + $message = 'Your browser does not support the video tag.'; + $video = video($target, $message, 'controls', $this->tracks); $this->assertEquals($expected, $video); + } + public function testVideoWithTracksAndIndex() + { $expected = << +
www.codeigniter.com test'], 'test02' => ['This is my noreply@codeigniter.com test', - "This is my test"], + "This is my test"], 'test03' => ['
www.google.com', '
www.google.com'], 'test04' => ['Download CodeIgniter at www.codeigniter.com. Period test.', @@ -805,9 +797,9 @@ public function autolinkBoth() 'test06' => ['This one: ://codeigniter.com must not break this one: http://codeigniter.com', 'This one: ://codeigniter.com must not break this one: http://codeigniter.com'], 'test07' => ['Visit example.com or email foo@bar.com', - "Visit example.com or email "], + "Visit example.com or email "], 'test08' => ['Visit www.example.com or email foo@bar.com', - "Visit www.example.com or email "], + "Visit www.example.com or email "], ]; } @@ -856,6 +848,8 @@ public function testPrepUrl() { $this->assertEquals('http://codeigniter.com', prep_url('codeigniter.com')); $this->assertEquals('http://www.codeigniter.com', prep_url('www.codeigniter.com')); + $this->assertEquals('', prep_url()); + $this->assertEquals('http://www.codeigniter.com', prep_url('http://www.codeigniter.com')); } //-------------------------------------------------------------------- diff --git a/tests/system/Test/FeatureResponseTest.php b/tests/system/Test/FeatureResponseTest.php index 49ce991424e1..9ea02369d258 100644 --- a/tests/system/Test/FeatureResponseTest.php +++ b/tests/system/Test/FeatureResponseTest.php @@ -142,7 +142,7 @@ public function testAssertSessionMissing() public function testAssertHeader() { - $this->getFeatureResponse('

Hello World

', null, ['foo' => 'bar']); + $this->getFeatureResponse('

Hello World

', [], ['foo' => 'bar']); $this->feature->assertHeader('foo'); $this->feature->assertHeader('foo', 'bar'); @@ -237,7 +237,7 @@ public function testJsonExactString() } - protected function getFeatureResponse($body=null, array $responseOptions = null, array $headers = null) + protected function getFeatureResponse($body=null, array $responseOptions = [], array $headers = []) { $this->response = new Response(new \Config\App()); $this->response->setBody($body); diff --git a/tests/system/Test/FeatureTestCaseTest.php b/tests/system/Test/FeatureTestCaseTest.php index 89f4395f6dce..a2c318ef0de6 100644 --- a/tests/system/Test/FeatureTestCaseTest.php +++ b/tests/system/Test/FeatureTestCaseTest.php @@ -16,33 +16,33 @@ public function setUp() $this->skipEvents(); } - public function testCallSimpleGet() + public function testCallGet() { $this->withRoutes([ - ['add', 'home', function() { + ['get', 'home', function() { return 'Hello World'; }] ]); - $response = $this->call('get', 'home'); + $response = $this->get('home'); - $this->assertInstanceOf(FeatureResponse::class, $response); - $this->assertInstanceOf(\CodeIgniter\HTTP\Response::class, $response->response); - $this->assertTrue($response->isOK()); - $this->assertEquals('Hello World', $response->response->getBody()); - $this->assertEquals(200, $response->response->getStatusCode()); + $response->assertSee('Hello World'); + $response->assertDontSee('Again'); } - public function testCallGet() + public function testCallSimpleGet() { $this->withRoutes([ - ['get', 'home', function() { + ['add', 'home', function() { return 'Hello World'; }] ]); - $response = $this->get('home'); + $response = $this->call('get', 'home'); - $response->assertSee('Hello World'); - $response->assertDontSee('Again'); + $this->assertInstanceOf(FeatureResponse::class, $response); + $this->assertInstanceOf(\CodeIgniter\HTTP\Response::class, $response->response); + $this->assertTrue($response->isOK()); + $this->assertEquals('Hello World', $response->response->getBody()); + $this->assertEquals(200, $response->response->getStatusCode()); } public function testCallPost() diff --git a/tests/system/Validation/RulesTest.php b/tests/system/Validation/RulesTest.php index 414249867f76..62f871cee216 100644 --- a/tests/system/Validation/RulesTest.php +++ b/tests/system/Validation/RulesTest.php @@ -1,856 +1,858 @@ - [ + \CodeIgniter\Validation\Rules::class, + \CodeIgniter\Validation\FormatRules::class, + \CodeIgniter\Validation\FileRules::class, + \CodeIgniter\Validation\CreditCardRules::class, + \Tests\Support\Validation\TestRules::class, + ], + 'groupA' => [ + 'foo' => 'required|min_length[5]', + ], + 'groupA_errors' => [ + 'foo' => [ + 'min_length' => 'Shame, shame. Too short.', + ], + ], + ]; + + //-------------------------------------------------------------------- + + public function setUp() + { + parent::setUp(); + + $this->validation = new Validation((object) $this->config, \Config\Services::renderer()); + $this->validation->reset(); + + $_FILES = []; + } + + //-------------------------------------------------------------------- + // Rules Tests + //-------------------------------------------------------------------- + + public function testRequiredNull() + { + $data = [ + 'foo' => null, + ]; + + $this->validation->setRules([ + 'foo' => 'required', + ]); + + $this->assertFalse($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testRequiredTrueString() + { + $data = [ + 'foo' => 123, + ]; + + $this->validation->setRules([ + 'foo' => 'required', + ]); + + $this->assertTrue($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testRequiredFalseString() + { + $data = [ + 'bar' => 123, + ]; + + $this->validation->setRules([ + 'foo' => 'required', + ]); + + $this->assertFalse($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testRequiredTrueArray() + { + $data = [ + 'foo' => [123], + ]; + + $this->validation->setRules([ + 'foo' => 'required', + ]); + + $this->assertTrue($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testRequiredFalseArray() + { + $data = [ + 'foo' => [], + ]; + + $this->validation->setRules([ + 'foo' => 'required', + ]); + + $this->assertFalse($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testRequiredObject() + { + $data = [ + 'foo' => new \stdClass(), + ]; + + $this->validation->setRules([ + 'foo' => 'required', + ]); + + $this->assertTrue($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + /** + * @dataProvider ifExistProvider + * + * @param $rules + * @param $data + * @param $expected + */ + public function testIfExist($rules, $data, $expected) + { + $this->validation->setRules($rules); + + $this->assertEquals($expected, $this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function ifExistProvider() + { + return [ + [['foo' => 'required'], ['foo' => ''], false], + [['foo' => 'required'], [], false], + [['foo' => 'if_exist|required'], ['foo' => ''], false], + // Input data does not exist then the other rules will be ignored + [['foo' => 'if_exist|required'], [], true], + ]; + } + + //-------------------------------------------------------------------- + + /** + * @dataProvider emptysProvider + * + * @param $rules + * @param $data + * @param $expected + */ + public function testEmptys($rules, $data, $expected) + { + $this->validation->setRules($rules); + + $this->assertEquals($expected, $this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function emptysProvider() + { + return [ + [['foo' => 'permit_empty'], ['foo' => ''], true], + [['foo' => 'permit_empty'], ['foo' => '0'], true], + [['foo' => 'permit_empty'], ['foo' => 0], true], + [['foo' => 'permit_empty'], ['foo' => 0.0], true], + [['foo' => 'permit_empty'], ['foo' => null], true], + [['foo' => 'permit_empty'], ['foo' => false], true], + [['foo' => 'permit_empty|valid_email'], ['foo' => ''], true], + [['foo' => 'permit_empty|valid_email'], ['foo' => 'user@domain.tld'], true], + [['foo' => 'permit_empty|valid_email'], ['foo' => 'invalid'], false], + // Required has more priority + [['foo' => 'permit_empty|required|valid_email'], ['foo' => ''], false], + [['foo' => 'permit_empty|required'], ['foo' => ''], false], + [['foo' => 'permit_empty|required'], ['foo' => null], false], + [['foo' => 'permit_empty|required'], ['foo' => false], false], + // This tests will return true because the input data is trimmed + [['foo' => 'permit_empty|required'], ['foo' => '0'], true], + [['foo' => 'permit_empty|required'], ['foo' => 0], true], + [['foo' => 'permit_empty|required'], ['foo' => 0.0], true], + ]; + } + + //-------------------------------------------------------------------- + + public function testMatchesNull() + { + $data = [ + 'foo' => null, + 'bar' => null, + ]; + + $this->validation->setRules([ + 'foo' => 'matches[bar]', + ]); + + $this->assertTrue($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testMatchesTrue() + { + $data = [ + 'foo' => 'match', + 'bar' => 'match', + ]; + + $this->validation->setRules([ + 'foo' => 'matches[bar]', + ]); + + $this->assertTrue($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testMatchesFalse() + { + $data = [ + 'foo' => 'match', + 'bar' => 'nope', + ]; + + $this->validation->setRules([ + 'foo' => 'matches[bar]', + ]); + + $this->assertFalse($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testDiffersNull() + { + $data = [ + 'foo' => null, + 'bar' => null, + ]; + + $this->validation->setRules([ + 'foo' => 'differs[bar]', + ]); + + $this->assertFalse($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testDiffersTrue() + { + $data = [ + 'foo' => 'match', + 'bar' => 'nope', + ]; + + $this->validation->setRules([ + 'foo' => 'differs[bar]', + ]); + + $this->assertTrue($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testDiffersFalse() + { + $data = [ + 'foo' => 'match', + 'bar' => 'match', + ]; + + $this->validation->setRules([ + 'foo' => 'differs[bar]', + ]); + + $this->assertFalse($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + /** + * @group DatabaseLive + */ + public function testIsUniqueFalse() + { + $db = Database::connect(); + $db->table('user')->insert([ + 'name' => 'Derek Travis', + 'email' => 'derek@world.com', + 'country' => 'Elbonia', + ]); + + $data = [ + 'email' => 'derek@world.com', + ]; + + $this->validation->setRules([ + 'email' => 'is_unique[user.email]', + ]); + + $this->assertFalse($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + /** + * @group DatabaseLive + */ + public function testIsUniqueTrue() + { + $data = [ + 'email' => 'derek@world.co.uk', + ]; + + $this->validation->setRules([ + 'email' => 'is_unique[user.email]', + ]); + + $this->assertTrue($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + /** + * @group DatabaseLive + */ + public function testIsUniqueIgnoresParams() + { + $db = Database::connect(); + $user = $db->table('user') + ->insert([ + 'name' => 'Developer A', + 'email' => 'deva@example.com', + 'country' => 'Elbonia', + ]); + $row = $db->table('user') + ->limit(1) + ->get() + ->getRow(); + + $data = [ + 'email' => 'derek@world.co.uk', + ]; + + $this->validation->setRules([ + 'email' => "is_unique[user.email,id,{$row->id}]", + ]); + + $this->assertTrue($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testMinLengthNull() + { + $data = [ + 'foo' => null, + ]; + + $this->validation->setRules([ + 'foo' => 'min_length[3]', + ]); + + $this->assertFalse($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testMinLengthReturnsFalseWithNonNumericVal() + { + $data = [ + 'foo' => 'bar', + ]; + + $this->validation->setRules([ + 'foo' => 'min_length[bar]', + ]); + + $this->assertFalse($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testMinLengthReturnsTrueWithSuccess() + { + $data = [ + 'foo' => 'bar', + ]; - /** - * @var Validation - */ - protected $validation; - protected $config = [ - 'ruleSets' => [ - \CodeIgniter\Validation\Rules::class, - \CodeIgniter\Validation\FormatRules::class, - \CodeIgniter\Validation\FileRules::class, - \CodeIgniter\Validation\CreditCardRules::class, - \Tests\Support\Validation\TestRules::class, - ], - 'groupA' => [ - 'foo' => 'required|min_length[5]', - ], - 'groupA_errors' => [ - 'foo' => [ - 'min_length' => 'Shame, shame. Too short.', - ], - ], - ]; - - //-------------------------------------------------------------------- - - public function setUp() - { - parent::setUp(); - - $this->validation = new Validation((object)$this->config, \Config\Services::renderer()); - $this->validation->reset(); - - $_FILES = []; - } - - //-------------------------------------------------------------------- - // Rules Tests - //-------------------------------------------------------------------- - - public function testRequiredNull() - { - $data = [ - 'foo' => null, - ]; - - $this->validation->setRules([ - 'foo' => 'required', - ]); - - $this->assertFalse($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testRequiredTrueString() - { - $data = [ - 'foo' => 123, - ]; - - $this->validation->setRules([ - 'foo' => 'required', - ]); - - $this->assertTrue($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testRequiredFalseString() - { - $data = [ - 'bar' => 123, - ]; - - $this->validation->setRules([ - 'foo' => 'required', - ]); - - $this->assertFalse($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testRequiredTrueArray() - { - $data = [ - 'foo' => [123], - ]; - - $this->validation->setRules([ - 'foo' => 'required', - ]); - - $this->assertTrue($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testRequiredFalseArray() - { - $data = [ - 'foo' => [], - ]; - - $this->validation->setRules([ - 'foo' => 'required', - ]); - - $this->assertFalse($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testRequiredObject() - { - $data = [ - 'foo' => new \stdClass(), - ]; - - $this->validation->setRules([ - 'foo' => 'required', - ]); - - $this->assertTrue($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - /** - * @dataProvider ifExistProvider - * - * @param $rules - * @param $data - * @param $expected - */ - public function testIfExist($rules, $data, $expected) - { - $this->validation->setRules($rules); - - $this->assertEquals($expected, $this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function ifExistProvider() - { - return [ - [['foo' => 'required'], ['foo' => ''], false], - [['foo' => 'required'], [], false], - [['foo' => 'if_exist|required'], ['foo' => ''], false], - // Input data does not exist then the other rules will be ignored - [['foo' => 'if_exist|required'], [], true], - ]; - } - - //-------------------------------------------------------------------- - - /** - * @dataProvider emptysProvider - * - * @param $rules - * @param $data - * @param $expected - */ - public function testEmptys($rules, $data, $expected) - { - $this->validation->setRules($rules); - - $this->assertEquals($expected, $this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function emptysProvider() - { - return [ - [['foo' => 'permit_empty'], ['foo' => ''], true], - [['foo' => 'permit_empty'], ['foo' => '0'], true], - [['foo' => 'permit_empty'], ['foo' => 0], true], - [['foo' => 'permit_empty'], ['foo' => 0.0], true], - [['foo' => 'permit_empty'], ['foo' => null], true], - [['foo' => 'permit_empty'], ['foo' => false], true], - [['foo' => 'permit_empty|valid_email'], ['foo' => ''], true], - [['foo' => 'permit_empty|valid_email'], ['foo' => 'user@domain.tld'], true], - [['foo' => 'permit_empty|valid_email'], ['foo' => 'invalid'], false], - // Required has more priority - [['foo' => 'permit_empty|required|valid_email'], ['foo' => ''], false], - [['foo' => 'permit_empty|required'], ['foo' => ''], false], - [['foo' => 'permit_empty|required'], ['foo' => null], false], - [['foo' => 'permit_empty|required'], ['foo' => false], false], - // This tests will return true because the input data is trimmed - [['foo' => 'permit_empty|required'], ['foo' => '0'], true], - [['foo' => 'permit_empty|required'], ['foo' => 0], true], - [['foo' => 'permit_empty|required'], ['foo' => 0.0], true], - ]; - } - - //-------------------------------------------------------------------- - - public function testMatchesNull() - { - $data = [ - 'foo' => null, - 'bar' => null, - ]; - - $this->validation->setRules([ - 'foo' => 'matches[bar]', - ]); - - $this->assertTrue($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testMatchesTrue() - { - $data = [ - 'foo' => 'match', - 'bar' => 'match', - ]; - - $this->validation->setRules([ - 'foo' => 'matches[bar]', - ]); - - $this->assertTrue($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testMatchesFalse() - { - $data = [ - 'foo' => 'match', - 'bar' => 'nope', - ]; - - $this->validation->setRules([ - 'foo' => 'matches[bar]', - ]); - - $this->assertFalse($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testDiffersNull() - { - $data = [ - 'foo' => null, - 'bar' => null, - ]; - - $this->validation->setRules([ - 'foo' => 'differs[bar]', - ]); - - $this->assertFalse($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testDiffersTrue() - { - $data = [ - 'foo' => 'match', - 'bar' => 'nope', - ]; - - $this->validation->setRules([ - 'foo' => 'differs[bar]', - ]); - - $this->assertTrue($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testDiffersFalse() - { - $data = [ - 'foo' => 'match', - 'bar' => 'match', - ]; - - $this->validation->setRules([ - 'foo' => 'differs[bar]', - ]); - - $this->assertFalse($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - /** - * @group DatabaseLive - */ - public function testIsUniqueFalse() - { - $db = Database::connect(); - $db->table('user')->insert([ - 'name' => 'Derek Travis', - 'email' => 'derek@world.com' - ]); - - $data = [ - 'email' => 'derek@world.com', - ]; - - $this->validation->setRules([ - 'email' => 'is_unique[user.email]', - ]); - - $this->assertFalse($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - /** - * @group DatabaseLive - */ - public function testIsUniqueTrue() - { - $data = [ - 'email' => 'derek@world.co.uk', - ]; - - $this->validation->setRules([ - 'email' => 'is_unique[user.email]', - ]); - - $this->assertTrue($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - /** - * @group DatabaseLive - */ - public function testIsUniqueIgnoresParams() - { - $db = Database::connect(); - $user = $db->table('user') - ->insert([ - 'name' => 'Developer A', - 'email' => 'deva@example.com', - ]); - $row = $db->table('user') - ->limit(1) - ->get() - ->getRow(); - - $data = [ - 'email' => 'derek@world.co.uk', - ]; - - $this->validation->setRules([ - 'email' => "is_unique[user.email,id,{$row->id}]", - ]); - - $this->assertTrue($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testMinLengthNull() - { - $data = [ - 'foo' => null, - ]; - - $this->validation->setRules([ - 'foo' => 'min_length[3]', - ]); - - $this->assertFalse($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testMinLengthReturnsFalseWithNonNumericVal() - { - $data = [ - 'foo' => 'bar', - ]; - - $this->validation->setRules([ - 'foo' => 'min_length[bar]', - ]); - - $this->assertFalse($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testMinLengthReturnsTrueWithSuccess() - { - $data = [ - 'foo' => 'bar', - ]; - - $this->validation->setRules([ - 'foo' => 'min_length[2]', - ]); - - $this->assertTrue($this->validation->run($data)); - } - - //-------------------------------------------------------------------- + $this->validation->setRules([ + 'foo' => 'min_length[2]', + ]); + + $this->assertTrue($this->validation->run($data)); + } - public function testMinLengthReturnsTrueWithExactLength() - { - $data = [ - 'foo' => 'bar', - ]; + //-------------------------------------------------------------------- - $this->validation->setRules([ - 'foo' => 'min_length[3]', - ]); - - $this->assertTrue($this->validation->run($data)); - } + public function testMinLengthReturnsTrueWithExactLength() + { + $data = [ + 'foo' => 'bar', + ]; - //-------------------------------------------------------------------- + $this->validation->setRules([ + 'foo' => 'min_length[3]', + ]); + + $this->assertTrue($this->validation->run($data)); + } - public function testMinLengthReturnsFalseWhenWrong() - { - $data = [ - 'foo' => 'bar', - ]; + //-------------------------------------------------------------------- - $this->validation->setRules([ - 'foo' => 'min_length[4]', - ]); + public function testMinLengthReturnsFalseWhenWrong() + { + $data = [ + 'foo' => 'bar', + ]; - $this->assertFalse($this->validation->run($data)); - } + $this->validation->setRules([ + 'foo' => 'min_length[4]', + ]); - //-------------------------------------------------------------------- + $this->assertFalse($this->validation->run($data)); + } - public function testMaxLengthNull() - { - $data = [ - 'foo' => null, - ]; + //-------------------------------------------------------------------- - $this->validation->setRules([ - 'foo' => 'max_length[1]', - ]); + public function testMaxLengthNull() + { + $data = [ + 'foo' => null, + ]; - $this->assertTrue($this->validation->run($data)); - } + $this->validation->setRules([ + 'foo' => 'max_length[1]', + ]); - //-------------------------------------------------------------------- + $this->assertTrue($this->validation->run($data)); + } - public function testMaxLengthReturnsFalseWithNonNumericVal() - { - $data = [ - 'foo' => 'bar', - ]; + //-------------------------------------------------------------------- - $this->validation->setRules([ - 'foo' => 'max_length[bar]', - ]); + public function testMaxLengthReturnsFalseWithNonNumericVal() + { + $data = [ + 'foo' => 'bar', + ]; - $this->assertFalse($this->validation->run($data)); - } + $this->validation->setRules([ + 'foo' => 'max_length[bar]', + ]); - //-------------------------------------------------------------------- + $this->assertFalse($this->validation->run($data)); + } - public function testMaxLengthReturnsTrueWithSuccess() - { - $data = [ - 'foo' => 'bar', - ]; + //-------------------------------------------------------------------- - $this->validation->setRules([ - 'foo' => 'max_length[4]', - ]); + public function testMaxLengthReturnsTrueWithSuccess() + { + $data = [ + 'foo' => 'bar', + ]; - $this->assertTrue($this->validation->run($data)); - } + $this->validation->setRules([ + 'foo' => 'max_length[4]', + ]); - //-------------------------------------------------------------------- + $this->assertTrue($this->validation->run($data)); + } - public function testMaxLengthReturnsTrueWithExactLength() - { - $data = [ - 'foo' => 'bar', - ]; + //-------------------------------------------------------------------- - $this->validation->setRules([ - 'foo' => 'max_length[3]', - ]); + public function testMaxLengthReturnsTrueWithExactLength() + { + $data = [ + 'foo' => 'bar', + ]; - $this->assertTrue($this->validation->run($data)); - } + $this->validation->setRules([ + 'foo' => 'max_length[3]', + ]); - //-------------------------------------------------------------------- + $this->assertTrue($this->validation->run($data)); + } - public function testMaxLengthReturnsFalseWhenWrong() - { - $data = [ - 'foo' => 'bar', - ]; + //-------------------------------------------------------------------- - $this->validation->setRules([ - 'foo' => 'max_length[2]', - ]); + public function testMaxLengthReturnsFalseWhenWrong() + { + $data = [ + 'foo' => 'bar', + ]; - $this->assertFalse($this->validation->run($data)); - } + $this->validation->setRules([ + 'foo' => 'max_length[2]', + ]); - //-------------------------------------------------------------------- + $this->assertFalse($this->validation->run($data)); + } - public function testExactLengthNull() - { - $data = [ - 'foo' => null, - ]; + //-------------------------------------------------------------------- - $this->validation->setRules([ - 'foo' => 'exact_length[3]', - ]); + public function testExactLengthNull() + { + $data = [ + 'foo' => null, + ]; - $this->assertFalse($this->validation->run($data)); - } + $this->validation->setRules([ + 'foo' => 'exact_length[3]', + ]); - //-------------------------------------------------------------------- + $this->assertFalse($this->validation->run($data)); + } - public function testExactLengthReturnsTrueOnSuccess() - { - $data = [ - 'foo' => 'bar', - ]; - - $this->validation->setRules([ - 'foo' => 'exact_length[3]', - ]); - - $this->assertTrue($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testExactLengthDetectsBadLength() - { - $data = [ - 'foo' => 'bar', - ]; - - $this->validation->setRules([ - 'foo' => 'exact_length[abc]', - ]); - - $this->assertFalse($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testExactLengthReturnsFalseWhenShort() - { - $data = [ - 'foo' => 'bar', - ]; - - $this->validation->setRules([ - 'foo' => 'exact_length[2]', - ]); - - $this->assertFalse($this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function testExactLengthReturnsFalseWhenLong() - { - $data = [ - 'foo' => 'bar', - ]; - - $this->validation->setRules([ - 'foo' => 'exact_length[4]', - ]); - - $this->assertFalse($this->validation->run($data)); - } - - //------------------------------------------------------------------- - - /** - * @dataProvider greaterThanProvider - * - * @param $str - * @param $expected - */ - public function testGreaterThan($first, $second, $expected) - { - $data = [ - 'foo' => $first, - ]; - - $this->validation->setRules([ - 'foo' => "greater_than[{$second}]", - ]); - - $this->assertEquals($expected, $this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function greaterThanProvider() - { - return [ - ['-10', '-11', true], - ['10', '9', true], - ['10', '10', false], - ['10', 'a', false], - ['10a', '10', false], - [null, null, false] - ]; - } - - //------------------------------------------------------------------- - - /** - * @dataProvider greaterThanEqualProvider - * - * @param $str - * @param $expected - */ - public function testGreaterThanEqual($first, $second, $expected) - { - $data = [ - 'foo' => $first, - ]; - - $this->validation->setRules([ - 'foo' => "greater_than_equal_to[{$second}]", - ]); - - $this->assertEquals($expected, $this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function greaterThanEqualProvider() - { - return [ - ['0', '0', true], - ['1', '0', true], - ['-1', '0', false], - ['10a', '0', false], - [null, null, false], - [1, null, true], - [null, 1, false] - ]; - } - - //------------------------------------------------------------------- - - /** - * @dataProvider lessThanProvider - * - * @param $str - * @param $expected - */ - public function testLessThan($first, $second, $expected) - { - $data = [ - 'foo' => $first, - ]; - - $this->validation->setRules([ - 'foo' => "less_than[{$second}]", - ]); - - $this->assertEquals($expected, $this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function lessThanProvider() - { - return [ - ['4', '5', true], - ['-1', '0', true], - ['4', '4', false], - ['10a', '5', false], - [null, null, false], - [1, null, false], - [null, 1, false] - ]; - } - - //------------------------------------------------------------------- - - /** - * @dataProvider lessThanEqualProvider - * - * @param $str - * @param $expected - */ - public function testLessEqualThan($first, $second, $expected) - { - $data = [ - 'foo' => $first, - ]; - - $this->validation->setRules([ - 'foo' => "less_than_equal_to[{$second}]", - ]); - - $this->assertEquals($expected, $this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function lessThanEqualProvider() - { - return [ - ['-1', '0', true], - ['-1', '-1', true], - ['4', '4', true], - ['0', '-1', false], - ['10a', '0', false], - [null, null, false], - [null, 1, false], - [1, null, false] - ]; - } - - //------------------------------------------------------------------- - - /** - * @dataProvider inListProvider - * - * @param $str - * @param $expected - */ - public function testInList($first, $second, $expected) - { - $data = [ - 'foo' => $first, - ]; - - $this->validation->setRules([ - 'foo' => "in_list[{$second}]", - ]); - - $this->assertEquals($expected, $this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function inListProvider() - { - return [ - ['red', 'red,Blue,123', true], - ['Blue', 'red, Blue,123', true], - ['Blue', 'red,Blue,123', true], - ['123', 'red,Blue,123', true], - ['Red', 'red,Blue,123', false], - [' red', 'red,Blue,123', false], - ['1234', 'red,Blue,123', false], - [null, 'red,Blue,123', false], - ['red', null, false], - ]; - } - - //-------------------------------------------------------------------- - - /** - * @dataProvider requiredWithProvider - * - * @param $check - * @param $expected - */ - public function testRequiredWith($field, $check, $expected = false) - { - $data = [ - 'foo' => 'bar', - 'bar' => 'something', - 'baz' => null, - 'ar' => [] // Was running into issues with array values - ]; - - $this->validation->setRules([ - $field => "required_with[{$check}]", - ]); - - $this->assertEquals($expected, $this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function requiredWithProvider() - { - return [ - ['nope', 'bar', false], - ['foo', 'bar', true], - ['nope', 'baz', true], - [null, null, true], - [null, 'foo', false], - ['foo', null, true] - ]; - } - - //-------------------------------------------------------------------- - - /** - * @dataProvider requiredWithoutProvider - * - * @param $check - * @param $expected - */ - public function testRequiredWithout($field, $check, $expected = false) - { - $data = [ - 'foo' => 'bar', - 'bar' => 'something', - 'baz' => null, - ]; - - $this->validation->setRules([ - $field => "required_without[{$check}]", - ]); - - $this->assertEquals($expected, $this->validation->run($data)); - } - - //-------------------------------------------------------------------- - - public function requiredWithoutProvider() - { - return [ - ['nope', 'bars', false], - ['foo', 'nope', true], - [null, null, false], - [null, 'foo', true], - ['foo', null, true] - ]; - } - - //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + public function testExactLengthReturnsTrueOnSuccess() + { + $data = [ + 'foo' => 'bar', + ]; + + $this->validation->setRules([ + 'foo' => 'exact_length[3]', + ]); + + $this->assertTrue($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testExactLengthDetectsBadLength() + { + $data = [ + 'foo' => 'bar', + ]; + + $this->validation->setRules([ + 'foo' => 'exact_length[abc]', + ]); + + $this->assertFalse($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testExactLengthReturnsFalseWhenShort() + { + $data = [ + 'foo' => 'bar', + ]; + + $this->validation->setRules([ + 'foo' => 'exact_length[2]', + ]); + + $this->assertFalse($this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function testExactLengthReturnsFalseWhenLong() + { + $data = [ + 'foo' => 'bar', + ]; + + $this->validation->setRules([ + 'foo' => 'exact_length[4]', + ]); + + $this->assertFalse($this->validation->run($data)); + } + + //------------------------------------------------------------------- + + /** + * @dataProvider greaterThanProvider + * + * @param $str + * @param $expected + */ + public function testGreaterThan($first, $second, $expected) + { + $data = [ + 'foo' => $first, + ]; + + $this->validation->setRules([ + 'foo' => "greater_than[{$second}]", + ]); + + $this->assertEquals($expected, $this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function greaterThanProvider() + { + return [ + ['-10', '-11', true], + ['10', '9', true], + ['10', '10', false], + ['10', 'a', false], + ['10a', '10', false], + [null, null, false] + ]; + } + + //------------------------------------------------------------------- + + /** + * @dataProvider greaterThanEqualProvider + * + * @param $str + * @param $expected + */ + public function testGreaterThanEqual($first, $second, $expected) + { + $data = [ + 'foo' => $first, + ]; + + $this->validation->setRules([ + 'foo' => "greater_than_equal_to[{$second}]", + ]); + + $this->assertEquals($expected, $this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function greaterThanEqualProvider() + { + return [ + ['0', '0', true], + ['1', '0', true], + ['-1', '0', false], + ['10a', '0', false], + [null, null, false], + [1, null, true], + [null, 1, false] + ]; + } + + //------------------------------------------------------------------- + + /** + * @dataProvider lessThanProvider + * + * @param $str + * @param $expected + */ + public function testLessThan($first, $second, $expected) + { + $data = [ + 'foo' => $first, + ]; + + $this->validation->setRules([ + 'foo' => "less_than[{$second}]", + ]); + + $this->assertEquals($expected, $this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function lessThanProvider() + { + return [ + ['4', '5', true], + ['-1', '0', true], + ['4', '4', false], + ['10a', '5', false], + [null, null, false], + [1, null, false], + [null, 1, false] + ]; + } + + //------------------------------------------------------------------- + + /** + * @dataProvider lessThanEqualProvider + * + * @param $str + * @param $expected + */ + public function testLessEqualThan($first, $second, $expected) + { + $data = [ + 'foo' => $first, + ]; + + $this->validation->setRules([ + 'foo' => "less_than_equal_to[{$second}]", + ]); + + $this->assertEquals($expected, $this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function lessThanEqualProvider() + { + return [ + ['-1', '0', true], + ['-1', '-1', true], + ['4', '4', true], + ['0', '-1', false], + ['10a', '0', false], + [null, null, false], + [null, 1, false], + [1, null, false] + ]; + } + + //------------------------------------------------------------------- + + /** + * @dataProvider inListProvider + * + * @param $str + * @param $expected + */ + public function testInList($first, $second, $expected) + { + $data = [ + 'foo' => $first, + ]; + + $this->validation->setRules([ + 'foo' => "in_list[{$second}]", + ]); + + $this->assertEquals($expected, $this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function inListProvider() + { + return [ + ['red', 'red,Blue,123', true], + ['Blue', 'red, Blue,123', true], + ['Blue', 'red,Blue,123', true], + ['123', 'red,Blue,123', true], + ['Red', 'red,Blue,123', false], + [' red', 'red,Blue,123', false], + ['1234', 'red,Blue,123', false], + [null, 'red,Blue,123', false], + ['red', null, false], + ]; + } + + //-------------------------------------------------------------------- + + /** + * @dataProvider requiredWithProvider + * + * @param $check + * @param $expected + */ + public function testRequiredWith($field, $check, $expected = false) + { + $data = [ + 'foo' => 'bar', + 'bar' => 'something', + 'baz' => null, + 'ar' => [] // Was running into issues with array values + ]; + + $this->validation->setRules([ + $field => "required_with[{$check}]", + ]); + + $this->assertEquals($expected, $this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function requiredWithProvider() + { + return [ + ['nope', 'bar', false], + ['foo', 'bar', true], + ['nope', 'baz', true], + [null, null, true], + [null, 'foo', false], + ['foo', null, true] + ]; + } + + //-------------------------------------------------------------------- + + /** + * @dataProvider requiredWithoutProvider + * + * @param $check + * @param $expected + */ + public function testRequiredWithout($field, $check, $expected = false) + { + $data = [ + 'foo' => 'bar', + 'bar' => 'something', + 'baz' => null, + ]; + + $this->validation->setRules([ + $field => "required_without[{$check}]", + ]); + + $this->assertEquals($expected, $this->validation->run($data)); + } + + //-------------------------------------------------------------------- + + public function requiredWithoutProvider() + { + return [ + ['nope', 'bars', false], + ['foo', 'nope', true], + [null, null, false], + [null, 'foo', true], + ['foo', null, true] + ]; + } + + //-------------------------------------------------------------------- } diff --git a/user_guide_src/source/_themes/sphinx_rtd_theme/static/css/badge_only.css b/user_guide_src/source/_themes/sphinx_rtd_theme/static/css/badge_only.css index 6362912b151c..19fd5e8a3a84 100644 --- a/user_guide_src/source/_themes/sphinx_rtd_theme/static/css/badge_only.css +++ b/user_guide_src/source/_themes/sphinx_rtd_theme/static/css/badge_only.css @@ -1,2 +1,2 @@ -.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} +.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} /*# sourceMappingURL=badge_only.css.map */ diff --git a/user_guide_src/source/_themes/sphinx_rtd_theme/static/css/theme.css b/user_guide_src/source/_themes/sphinx_rtd_theme/static/css/theme.css index 877aa0a0c702..2115c8467996 100644 --- a/user_guide_src/source/_themes/sphinx_rtd_theme/static/css/theme.css +++ b/user_guide_src/source/_themes/sphinx_rtd_theme/static/css/theme.css @@ -1,4 +1,4 @@ -* { +* { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box diff --git a/user_guide_src/source/database/migration.rst b/user_guide_src/source/database/migration.rst index 56a249a7afcf..f5565f891966 100644 --- a/user_guide_src/source/database/migration.rst +++ b/user_guide_src/source/database/migration.rst @@ -54,7 +54,7 @@ migrations go in the **application/Database/Migrations/** directory and have nam as *20121031100537_Add_blog.php*. :: - find([1,2,3]); -**findWhere()** - -Allows you to specify one or more criteria that must be matched against the data. Returns -all rows that match:: - - // Use simple where - $users = $userModel->findWhere('role_id >', '10'); - - // Use array of where values - $users = $userModel->findWhere([ - 'status' => 'active', - 'deleted' => 0 - ]); +If no parameters are passed in, will return all rows in that model's table, effectively acting +like findAll(), though less explicit. **findAll()** @@ -284,6 +273,22 @@ of the columns in $table, while the array's values are the values to save for th $userModel->update($id, $data); +Multiple records may be updated with a single call by passing an array of primary keys as the first parameter:: + + $data = [ + 'active' => 1 + ]; + + $userModel->update([1, 2, 3], $data); + +When you need a more flexible solution, you can leaven the parameters empty and it functions like the Query Builder's +update command, with the added benefit of validation, events, etc:: + + $userModel + ->whereIn('id', [1,2,3]) + ->set(['active' => 1] + ->update(); + **save()** This is a wrapper around the insert() and update() methods that handles inserting or updating the record @@ -380,22 +385,14 @@ Takes a primary key value as the first parameter and deletes the matching record If the model's $useSoftDeletes value is true, this will update the row to set 'deleted = 1'. You can force a permanent delete by setting the second parameter as true. -**deleteWhere()** - -Deletes multiple records from the model's table based on the criteria pass into the first two parameters. -:: +An array of primary keys can be passed in as the first parameter to delete multiple records at once:: - // Simple where - $userMdoel->deleteWhere('status', 'inactive'); + $userModel->delete([1,2,3]); - // Complex where - $userModel->deleteWhere([ - 'status' => 'inactive', - 'warn_lvl >=' => 50 - ]); +If no parameters are passed in, will act like the Query Builder's delete method, requiring a where call +previously:: -If the model's $useSoftDeletes value is true, this will update the rows to set 'deleted = 1'. You can force -a permanent delete by setting the third parameter as true. + $userModel->where('id', 12)->delete(); **purgeDeleted()** @@ -540,17 +537,17 @@ provides methods that allow you to do just that. Returns data from the next find*() method as associative arrays:: - $users = $userModel->asArray()->findWhere('status', 'active'); + $users = $userModel->asArray()->where('status', 'active')->findAll(); **asObject()** Returns data from the next find*() method as standard objects or custom class intances:: // Return as standard objects - $users = $userModel->asObject()->findWhere('status', 'active'); + $users = $userModel->asObject()->where('status', 'active')->findAll(); // Return as custom class instances - $users = $userModel->asObject('User')->findWhere('status', 'active'); + $users = $userModel->asObject('User')->where('status', 'active')->findAll(); Processing Large Amounts of Data -------------------------------- @@ -631,7 +628,6 @@ afterUpdate **id** = the primary key of the row being updated. afterFind Varies by find* method. See the following: - find() **id** = the primary key of the row being searched for. **data** = The resulting row of data, or null if no result found. -- findWhere() **data** = the resulting rows of data, or null if no result found. - findAll() **data** = the resulting rows of data, or null if no result found. **limit** = the number of rows to find. **offset** = the number of rows to skip during the search. @@ -639,15 +635,9 @@ afterFind Varies by find* method. See the following: beforeDelete Varies by delete* method. See the following: - delete() **id** = primary key of row being deleted. **purge** = boolean whether soft-delete rows should be hard deleted. -- deleteWhere() **key**/**value** = the key/value pair used to search for rows to delete. - **purge** = boolean whether soft-delete rows should be hard deleted. afterDelete Varies by delete* method. See the following: - delete() **id** = primary key of row being deleted. **purge** = boolean whether soft-delete rows should be hard deleted. **result** = the result of the delete() call on the Query Builder. **data** = unused. -- deleteWhere() **key**/**value** = the key/value pair used to search for rows to delete. - **purge** boolean whether soft-delete rows should be hard deleted. - **result** = the result of the delete() call on the Query Builder. - **data** = unused. ================ ========================================================================================================= diff --git a/user_guide_src/source/general/controllers.rst b/user_guide_src/source/general/controllers.rst index f39dd4ca1fa2..21684d45d5ae 100644 --- a/user_guide_src/source/general/controllers.rst +++ b/user_guide_src/source/general/controllers.rst @@ -233,34 +233,6 @@ your *application/Config/Routes.php* file. CodeIgniter also permits you to remap your URIs using its :doc:`URI Routing ` feature. -Class Constructors -================== - -If you intend to use a constructor in any of your Controllers, you -**MUST** place the following line of code in it:: - - parent::__construct(...$params); - -The reason this line is necessary is because your local constructor will -be overriding the one in the parent controller class so we need to -manually call it. - -Example:: - - " /> .. note:: If you use any of the form helper functions listed on this page, + and you pass values as an associative array, the form values will be automatically escaped, so there is no need to call this function. Use it only if you are creating your own - form elements. + form elements, which you would pass as strings. Available Functions =================== @@ -52,7 +53,7 @@ The following functions are available: .. php:function:: form_open([$action = ''[, $attributes = ''[, $hidden = array()]]]) :param string $action: Form action/target URI string - :param array $attributes: HTML attributes + :param mixed $attributes: HTML attributes, as an array or escaped string :param array $hidden: An array of hidden fields' definitions :returns: An HTML form opening tag :rtype: string @@ -106,15 +107,15 @@ The following functions are available: -.. php:function:: form_open_multipart([$action = ''[, $attributes = array()[, $hidden = array()]]]) +.. php:function:: form_open_multipart([$action = ''[, $attributes = ''[, $hidden = array()]]]) :param string $action: Form action/target URI string - :param array $attributes: HTML attributes + :param mixed $attributes: HTML attributes, as an array or escaped string :param array $hidden: An array of hidden fields' definitions :returns: An HTML multipart form opening tag :rtype: string - This function is absolutely identical to :php:func:`form_open()` above, + This function is identical to :php:func:`form_open()` above, except that it adds a *multipart* attribute, which is necessary if you would like to use the form to upload files with. @@ -289,7 +290,7 @@ The following functions are available: contain the name of the field, the second parameter will contain an associative array of options, and the third parameter will contain the value you wish to be selected. You can also pass an array of multiple - items through the third parameter, and CodeIgniter will create a + items through the third parameter, and the helper will create a multiple select for you. Example:: diff --git a/user_guide_src/source/helpers/html_helper.rst b/user_guide_src/source/helpers/html_helper.rst index 4bf6e0bf6d3c..d5ad51f6f23a 100755 --- a/user_guide_src/source/helpers/html_helper.rst +++ b/user_guide_src/source/helpers/html_helper.rst @@ -26,9 +26,9 @@ The following functions are available: .. php:function:: img([$src = ''[, $indexPage = false[, $attributes = '']]]) - :param string $src: Image source data + :param mixed $src: Image source data :param bool $indexPage: Whether to treat $src as a routed URI string - :param array $attributes: HTML attributes + :param mixed $attributes: HTML attributes :returns: HTML image tag :rtype: string @@ -46,7 +46,7 @@ The following functions are available: echo img('images/picture.jpg', true); // - Additionally, an associative array can be passed to the ``img()`` function + Additionally, an associative array can be passed as the first parameter, for complete control over all attributes and values. If an *alt* attribute is not provided, CodeIgniter will generate an empty string. @@ -96,7 +96,7 @@ The following functions are available: echo link_tag('feed', 'alternate', 'application/rss+xml', 'My RSS Feed'); // - Additionally, an associative array can be passed to the ``link()`` function + Alternately, an associative array can be passed to the ``link_tag()`` function for complete control over all attributes and values:: $link = array( @@ -111,7 +111,7 @@ The following functions are available: .. php:function:: script_tag([$src = ''[, $indexPage = false]]) - :param string $src: The source of JavaScript file + :param mixed $src: The source name of a JavaScript file :param bool $indexPage: Whether to treat $src as a routed URI string :returns: HTML script tag :rtype: string @@ -126,11 +126,7 @@ The following functions are available: echo script_tag('js/mystyles.js'); // - Further examples:: - - // to be done - - Additionally, an associative array can be passed to the ``script_tag()`` function + Alternately, an associative array can be passed to the ``script_tag()`` function for complete control over all attributes and values:: $script = array('src' => 'js/printer.js'); @@ -341,7 +337,7 @@ The following functions are available: :param string $attributes: :param array $tracks: Use the track function inside an array. See :php:func:`track()` function :param bool $indexPage: - :returns: HTML-formatted video element + :returns: HTML-formatted audio element :rtype: string Identical to :php:func:`video()`, only it produces the