diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 65254b61..6b413b85 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -12,25 +12,25 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2 - - name: Validate composer.json and composer.lock - run: composer validate --strict + - name: Validate composer.json and composer.lock + run: composer validate --strict - - name: Cache Composer packages - id: composer-cache - uses: actions/cache@v2 - with: - path: vendor - key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php- + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v2 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- - - name: Install dependencies - run: composer install --prefer-dist --no-progress + - name: Install dependencies + run: composer install --prefer-dist --no-progress - # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit" - # Docs: https://getcomposer.org/doc/articles/scripts.md + - name: Run test suite + run: php vendor/bin/codecept run --debug - # - name: Run test suite - # run: composer run-script test + - name: Check code style + run: php vendor/bin/phpcs diff --git a/.gitignore b/.gitignore index 262aabd7..c1cd27d9 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,5 @@ composer.lock # Tools composer.phar codecept.phar -codeception.yml # Vendors /vendor diff --git a/.scrutinizer.yml b/.scrutinizer.yml index d118bc65..e884aac8 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -31,10 +31,10 @@ tools: standard: PSR12 php_cpd: enabled: true - excluded_dirs: [bin, data, docs, docker, public, tests, vendor] + excluded_dirs: [ bin, data, docs, docker, public, tests, vendor ] php_loc: enabled: true - excluded_dirs: [bin, data, docs, docker, public, tests, vendor] + excluded_dirs: [ bin, data, docs, docker, public, tests, vendor ] php_mess_detector: true php_pdepend: true sensiolabs_security_checker: true diff --git a/.travis.sh b/.travis.sh index 90a47b41..a8801fc0 100644 --- a/.travis.sh +++ b/.travis.sh @@ -13,20 +13,20 @@ if [ "$TRAVIS_REPO_SLUG" == "bluzphp/framework" ] && [ "$TRAVIS_TAG" != "" ] && echo "Publishing" # move docs to `home` directory - cp -R docs/html $HOME/docs-latest + cp -R docs/html "$HOME/docs-latest" - cd $HOME + cd "$HOME" || exit git config --global user.email "travis@travis-ci.com" git config --global user.name "travis-ci" git config --global push.default simple - git clone --quiet https://${GITHUB_TOKEN}@github.com/bluzphp/bluzphp.github.io > /dev/null + git clone --quiet "https://$GITHUB_TOKEN@github.com/bluzphp/bluzphp.github.io" > /dev/null - cd bluzphp.github.io + cd bluzphp.github.io || exit echo "-- Clean" git rm -rf ./ > /dev/null echo "-- Copy" - cp -Rf $HOME/docs-latest/* ./ + cp -Rf "$HOME"/docs-latest/* ./ echo "-- Push" git add -f . diff --git a/.travis.yml b/.travis.yml index fac73e5a..d1b7dc24 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,13 @@ language: php php: - - 7.4 - - 8.0 - - 8.1 + - 8.2 + - 8.3 + - 8.4 services: - mysql matrix: allow_failures: - - php: 8.0 - - php: 8.1 + - php: 8.4 env: global: - XDEBUG_MODE=coverage @@ -51,4 +50,4 @@ notifications: - https://webhooks.gitter.im/e/c4fa557829c5bd992271 on_success: change # options: [always|never|change] default: always on_failure: always # options: [always|never|change] default: always - on_start: false # default: false + on_start: never # default: false diff --git a/README.md b/README.md index 7f7e07dc..047d88a0 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Easy to setup, easy to use. [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bluzphp/main) ## Achievements -[![PHP >= 7.4+](https://img.shields.io/packagist/php-v/bluzphp/framework.svg?style=flat)](https://php.net/) +[![PHP >= 8.1+](https://img.shields.io/packagist/php-v/bluzphp/framework.svg?style=flat)](https://php.net/) [![Latest Stable Version](https://img.shields.io/packagist/v/bluzphp/framework.svg?label=version&style=flat)](https://packagist.org/packages/bluzphp/framework) @@ -33,7 +33,10 @@ Auto-generating documentation is available at the http://bluzphp.github.io/ The project is developed by [NIX][3] PHP team and distributed under [MIT LICENSE][4] [1]: https://github.com/bluzphp/skeleton + [2]: https://github.com/bluzphp/framework/wiki -[3]: http://nixsolutions.com + +[3]: https://nixsolutions.com + [4]: https://raw.github.com/bluzphp/framework/master/LICENSE.md diff --git a/SECURITY.md b/SECURITY.md index 03970c1e..b9bf11b2 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,4 +2,4 @@ ## Reporting a Vulnerability -Please send email to anton.shevchuk@gmail.com +Please send an email to anton.shevchuk@gmail.com diff --git a/codeception.dist.yml b/codeception.yml similarity index 57% rename from codeception.dist.yml rename to codeception.yml index 0ad783f9..f8af3cd6 100644 --- a/codeception.dist.yml +++ b/codeception.yml @@ -1,21 +1,20 @@ -actor: Tester -bootstrap: _bootstrap.php +namespace: Bluz\Tests +support_namespace: Support paths: tests: tests - log: tests/_output - data: tests/_data - support: tests/_support + output: tests/_output + data: tests/Support/Data + support: tests/Support envs: tests/_envs -settings: - colors: true - memory_limit: 1024M +actor_suffix: Tester +bootstrap: _bootstrap.php extensions: enabled: - - Codeception\Extension\RunFailed + - Codeception\Extension\RunFailed modules: config: Db: - dsn: 'mysql:host=localhost;dbname=bluz' + dsn: 'sqlite:tests/framework.sqlite' user: 'root' password: '' dump: tests/_data/dump.sql @@ -24,4 +23,4 @@ modules: REST: url: http://127.0.0.1:8000/ depends: PhpBrowser - part: Json + part: Json \ No newline at end of file diff --git a/composer.json b/composer.json index 2bf5b11d..95ede077 100644 --- a/composer.json +++ b/composer.json @@ -1,52 +1,55 @@ { - "name": "bluzphp/framework", - "description": "Lightweight PHP framework", - "type": "library", - "require": { - "php": ">=7.4", - "ext-PDO": "*", - "ext-pdo_mysql": "*", - "ext-json": "*", - "ext-ctype": "*", - "bluzphp/collection": "~1.0", - "psr/log": "~1.1", - "laminas/laminas-diactoros": "~2.6", - "laminas/laminas-httphandlerrunner": "~1.4", - "symfony/cache": "^5.3" + "name": "bluzphp/framework", + "description": "Lightweight PHP framework", + "type": "library", + "require": { + "php": ">=8.2", + "ext-ctype": "*", + "ext-fileinfo": "*", + "ext-gettext": "*", + "ext-json": "*", + "ext-pdo": "*", + "ext-pdo_mysql": "*", + "bluzphp/collection": "~2.2", + "bluzphp/container": "~1.2", + "laminas/laminas-diactoros": "~2.26", + "laminas/laminas-httphandlerrunner": "~2.10", + "psr/log": "~2.0", + "symfony/cache": "~6.4", + "symfony/validator": "~6.4" + }, + "require-dev": { + "codeception/codeception": "~5.1", + "codeception/module-asserts": "~3.0", + "codeception/module-phpbrowser": "~3.0", + "php-coveralls/php-coveralls": "~2.7", + "squizlabs/php_codesniffer": "~3.10", + "phploc/phploc": "~7.0" + }, + "suggest": { + "ext-redis": "required by Redis adapter for Bluz\\Cache (https://github.com/bluzphp/framework/wiki/Cache)", + "ext-memcached": "required by Memcached adapter for Bluz\\Cache (https://github.com/bluzphp/framework/wiki/Cache)", + "predis/predis": "required by Redis adapter for Bluz\\Cache (https://github.com/bluzphp/framework/wiki/Cache)" + }, + "autoload": { + "psr-4": { + "Bluz\\": "src/", + "Bluz\\Tests\\": "tests/" }, - "require-dev": { - "codeception/codeception": "~4.1", - "codeception/module-asserts": "~1.3", - "codeception/module-phpbrowser": "~1.0", - "php-coveralls/php-coveralls": "~2.4", - "squizlabs/php_codesniffer": "~3.6", - "phploc/phploc": "~7.0" - }, - "suggest": { - "ext-gettext": "required by Bluz\\Translator (https://github.com/bluzphp/framework/wiki/Translator)", - "ext-redis": "required by Redis adapter for Bluz\\Cache (https://github.com/bluzphp/framework/wiki/Cache)", - "ext-memcached": "required by Memcached adapter for Bluz\\Cache (https://github.com/bluzphp/framework/wiki/Cache)", - "phpmailer/phpmailer": "required by Bluz\\Mailer (https://github.com/bluzphp/framework/wiki/Mailer)", - "predis/predis": "required by Redis adapter for Bluz\\Cache (https://github.com/bluzphp/framework/wiki/Cache)" - }, - "autoload": { - "psr-4": { - "Bluz\\": "src/" - }, - "files": [ - "src/_functions.php", - "src/_loader.php" - ] - }, - "authors": [ - { - "name": "Bluz Framework Contributors", - "homepage": "https://github.com/bluzphp/framework/graphs/contributors" - } - ], - "support": { - "issues": "https://github.com/bluzphp/framework/issues", - "wiki": "https://github.com/bluzphp/framework/wiki" - }, - "license": "MIT" + "files": [ + "src/_functions.php", + "src/_loader.php" + ] + }, + "authors": [ + { + "name": "Bluz Framework Contributors", + "homepage": "https://github.com/bluzphp/framework/graphs/contributors" + } + ], + "support": { + "issues": "https://github.com/bluzphp/framework/issues", + "wiki": "https://github.com/bluzphp/framework/wiki" + }, + "license": "MIT" } diff --git a/docs/DockBlock.php b/docs/DockBlock.php index 862a039e..c27e8caf 100644 --- a/docs/DockBlock.php +++ b/docs/DockBlock.php @@ -1,6 +1,7 @@ ] * @.deprecated [version] [] * - * @param string $arg1 the string to quote - * @param int $arg2 an integer of how many problems happened. + * @param string $arg1 the string to quote + * @param int $arg2 an integer of how many problems happened. * Indent to the description's starting point * for long ones. * - * @return int the integer of the set mode used. FALSE if foo + * @return bool|int the integer of the set mode used. FALSE if foo * foo could not be set. * - * @throws \Exception if first argument is not a string - * + * @throws ExceptionAlias if first argument is not a string * @see DockBlock::$foo, DockBlock::setFoo() * @since 1.3.0 Added the $arg2 * @since 1.2.0 * @deprecated 2.0.0 */ - public function setFoo($arg1, $arg2 = 0) + public function setFoo(string $arg1, int $arg2 = 0): bool|int { /* * This is a "Block Comment." The format is the same as * Docblock Comments except there is only one asterisk at the * top. phpDocumentor doesn't parse these. */ - if (is_int($arg1)) { - throw new \Exception("First argument should be string"); + if ($arg2 < 0) { + throw new ExceptionAlias("Second argument should be great than zero"); } if ($arg1 == 'good' || $arg1 == 'fair') { diff --git a/phpcs.xml b/phpcs.xml index aab3932d..1509b74f 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -3,10 +3,7 @@ The coding standard for Bluz project ./src - ./tests/src - - ./tests/src/Fixtures - ./tests/src/Common/Fixtures + ./tests/ \ \ No newline at end of file diff --git a/src/Application/Application.php b/src/Application/Application.php index 07e53358..0677bd5c 100644 --- a/src/Application/Application.php +++ b/src/Application/Application.php @@ -11,40 +11,53 @@ namespace Bluz\Application; -use Bluz\Application\Exception\ApplicationException; -use Bluz\Config\ConfigException; -use Bluz\Config\ConfigLoader; -use Bluz\Http\Exception\ForbiddenException; -use Bluz\Http\Exception\RedirectException; -use Bluz\Common; use Bluz\Common\Exception\CommonException; use Bluz\Common\Exception\ComponentException; +use Bluz\Common\Exception\ConfigurationException; +use Bluz\Common\Helper; +use Bluz\Common\Nil; +use Bluz\Config\Config; +use Bluz\Config\ConfigException; +use Bluz\Config\ConfigLoader; use Bluz\Controller\Controller; use Bluz\Controller\ControllerException; -use Bluz\Proxy\Config; -use Bluz\Proxy\Layout; -use Bluz\Proxy\Logger; -use Bluz\Proxy\Messages; -use Bluz\Proxy\Request; -use Bluz\Proxy\Response; -use Bluz\Proxy\Router; -use Bluz\Proxy\Session; -use Bluz\Proxy\Translator; -use Bluz\Request\RequestFactory; -use Bluz\Response\Response as ResponseInstance; +use Bluz\Http\Exception\ForbiddenException; +use Bluz\Http\Exception\NotAcceptableException; +use Bluz\Http\Exception\NotAllowedException; +use Bluz\Http\Exception\RedirectException; +use Bluz\Http\MimeType; +use Bluz\Logger\Logger; +use Bluz\Messages\Messages; +use Bluz\Proxy\Acl as AclProxy; +use Bluz\Proxy\Application as ApplicationProxy; +use Bluz\Proxy\Cache as CacheProxy; +use Bluz\Proxy\Config as ConfigProxy; +use Bluz\Proxy\Layout as LayoutProxy; +use Bluz\Proxy\Logger as LoggerProxy; +use Bluz\Proxy\Messages as MessagesProxy; +use Bluz\Proxy\Request as RequestProxy; +use Bluz\Proxy\Response as ResponseProxy; +use Bluz\Proxy\Router as RouterProxy; +use Bluz\Proxy\Session as SessionProxy; +use Bluz\Proxy\Translator as TranslatorProxy; +use Bluz\Request\Request; +use Bluz\Response\ResponseType; +use Bluz\Response\Response; +use Bluz\Router\Router; +use Bluz\Session\Session; +use Bluz\Translator\Translator; use Exception; -use InvalidArgumentException; -use ReflectionClass; +use Psr\Cache\CacheException; use ReflectionException; -use Laminas\Diactoros\ServerRequest; +use Throwable; /** * Application * * @package Bluz\Application * @link https://github.com/bluzphp/framework/wiki/Application + * * @author Anton Shevchuk - * @created 06.07.11 16:25 * * @method Controller error(Exception $exception) * @method mixed forbidden(ForbiddenException $exception) @@ -52,32 +65,58 @@ */ class Application { - use Common\Helper; - use Common\Singleton; + use Helper; /** - * @var string Environment name + * @var bool Debug application flag */ - protected $environment = 'production'; + protected bool $debugFlag = false; /** - * @var string Application path + * @var bool Layout usage flag */ - protected $path; + protected bool $layoutFlag = true; /** - * @var bool Debug application flag + * @var Data of the Application, cached! */ - protected $debugFlag = false; + private Data $data; /** - * @var bool Layout usage flag + * @param string $path + * @param string $baseUrl + * @param string $environment + * @throws CommonException */ - protected $layoutFlag = true; + public function __construct( + protected string $path, + protected string $baseUrl = '/', + protected string $environment = 'production' + ) { + // initial default helper path + $this->addHelperPath(__DIR__ . '/Helper/'); + + // setup singleton instance + ApplicationProxy::setInstance($this); + } + + /** + * @return string + */ + public function getPath(): string + { + return $this->path; + } + + /** + * @return string + */ + public function getBaseUrl(): string + { + return $this->baseUrl; + } /** - * Get application environment - * * @return string */ public function getEnvironment(): string @@ -86,22 +125,13 @@ public function getEnvironment(): string } /** - * Get path to Application + * Return Application data * - * @return string + * @return Data */ - public function getPath(): string + public function getData(): Data { - if (!$this->path) { - if (defined('PATH_APPLICATION')) { - $this->path = PATH_APPLICATION; - } else { - $reflection = new ReflectionClass($this); - // 3 level up - $this->path = dirname($reflection->getFileName(), 3); - } - } - return $this->path; + return $this->data; } /** @@ -115,174 +145,241 @@ public function isDebug(): bool } /** - * Return/setup Layout Flag - * - * @param bool|null $flag + * Return Layout flag * * @return bool */ - public function useLayout(?bool $flag = null): bool + public function hasLayout(): bool { - if (is_bool($flag)) { - $this->layoutFlag = $flag; - } - return $this->layoutFlag; } + /** + * @return void + */ + public function enableLayout(): void + { + $this->layoutFlag = true; + } + + /** + * @return void + */ + public function disableLayout(): void + { + $this->layoutFlag = false; + } + /** * Initialize system packages * - * @param string $environment - * * @return void * @throws ApplicationException */ - public function init(string $environment = 'production'): void + public function init(): void { - $this->environment = $environment; - try { - // initial default helper path - $this->addHelperPath(__DIR__ . '/Helper/'); - - // init Config $this->initConfig(); + $this->initLogger(); - // first log message - Logger::info('app:init'); - - // init Session, start inside class (if needed) - Session::getInstance(); - - // init Messages - Messages::getInstance(); + { + $this->applyConfig(); + } - // init Request + $this->initCache(); + $this->initData(); + $this->initSession(); + $this->initMessages(); $this->initRequest(); - - // init Response $this->initResponse(); - - // init Translator - $this->initTranslator(); - - // init Router $this->initRouter(); - } catch (Exception $e) { + $this->initTranslator(); + } catch (Throwable $e) { throw new ApplicationException("Application can't be loaded: " . $e->getMessage()); } } /** - * Initial Request instance + * Initial Config Proxy * * @return void * @throws ConfigException */ protected function initConfig(): void { + // load and merge configs $loader = new ConfigLoader(); - $loader->setPath($this->getPath()); - $loader->setEnvironment($this->getEnvironment()); - $loader->load(); + $loader->load($this->getPath() . '/configs/default'); + $loader->load($this->getPath() . '/configs/' . $this->environment); - $config = new \Bluz\Config\Config(); + // initial Config instance + $config = new Config(); $config->setFromArray($loader->getConfig()); - Config::setInstance($config); + ConfigProxy::setInstance($config); + } - // setup configuration for current environment - if ($debug = Config::get('debug')) { - $this->debugFlag = (bool)$debug; + /** + * Initial Logger Proxy + * + * @return void + */ + protected function initLogger(): void + { + if (ConfigProxy::get('logger')) { + $logger = new Logger(); + } else { + $logger = new Nil(); } - // initial php settings - if ($ini = Config::get('php')) { + LoggerProxy::setInstance($logger); + } + + /** + * Apply Config settings + * + * @return void + */ + protected function applyConfig(): void + { + // setup debug option + $this->debugFlag = ConfigProxy::get('debug'); + LoggerProxy::info('app:init:debug:' . $this->debugFlag); + + // setup php settings + if ($ini = ConfigProxy::get('php')) { foreach ($ini as $key => $value) { $result = ini_set($key, $value); - Logger::info('app:init:php:' . $key . ':' . ($result ?: '---')); + LoggerProxy::info('app:init:php:' . $key . ':' . ($result ?: '---')); } } } /** - * Initial Request instance + * Initial Cache Proxy * * @return void - * @throws InvalidArgumentException + * @throws ComponentException */ - protected function initRequest(): void + protected function initCache(): void { - $request = RequestFactory::fromGlobals(); + if (ConfigProxy::get('cache', 'enabled') && $adapterName = ConfigProxy::get('cache', 'adapter')) { + $adapter = ConfigProxy::get('cache', 'pools', $adapterName); + if (!$adapter) { + throw new ComponentException("Class `Proxy\\Cache` required configuration for `$adapterName` adapter"); + } + if (!is_callable($adapter)) { + throw new ComponentException("Class `Proxy\\Cache` required callable function `$adapterName` adapter"); + } + $cache = $adapter(); + CacheProxy::setInstance($cache); + } + } - Request::setInstance($request); + /** + * Initial Session Proxy + * + * @return void + * @throws ApplicationException + * @throws ReflectionException + */ + protected function initData(): void + { + $this->data = new Data($this->path); + + if ($data = CacheProxy::get('application.data')) { + $this->data->setFromArray($data); + } else { + $this->data->init(); + CacheProxy::set('application.data', $this->data->toArray(), CacheProxy::TTL_NO_EXPIRY, ['system']); + } } /** - * Initial Response instance + * Initial Session Proxy * * @return void */ - protected function initResponse(): void + protected function initSession(): void { - $response = new ResponseInstance(); + $session = new Session(); + $session->setOptions(ConfigProxy::get('session')); + SessionProxy::setInstance($session); + } - Response::setInstance($response); + /** + * Initial Messages Proxy + * + * @return void + */ + private function initMessages(): void + { + $messages = new Messages(); + $messages->setOptions(ConfigProxy::get('messages')); + MessagesProxy::setInstance($messages); } /** - * Get Response instance + * Initial Request Proxy * - * @return ResponseInstance + * @return void */ - public function getResponse(): ResponseInstance + private function initRequest(): void { - return Response::getInstance(); + $request = new Request( + baseUrl: $this->baseUrl, + ); + RequestProxy::setInstance($request); } /** - * Get Request instance + * Initial Response Proxy * - * @return ServerRequest + * @return void */ - public function getRequest(): ServerRequest + private function initResponse(): void { - return Request::getInstance(); + $response = new Response(); + ResponseProxy::setInstance($response); } /** - * Initial Router instance + * Initial Router Proxy * * @return void */ protected function initRouter(): void { - $router = new \Bluz\Router\Router(); - $router->setOptions(Config::get('router')); - - Router::setInstance($router); + $router = new Router( + baseUrl: $this->baseUrl, + path: RequestProxy::getPath(), + modules: $this->getData()->getModules(), + routes: $this->getData()->getRoutes() + ); + $router->setOptions(ConfigProxy::get('router')); + RouterProxy::setInstance($router); } /** - * Initial Translator instance + * Initial Translator Proxy * * @return void - * @throws Common\Exception\ConfigurationException + * @throws ConfigurationException */ - protected function initTranslator(): void + private function initTranslator(): void { - $translator = new \Bluz\Translator\Translator(); - $translator->setOptions(Config::get('translator')); + $translator = new Translator(); + $translator->setOptions(ConfigProxy::get('translator')); $translator->init(); - Translator::setInstance($translator); + TranslatorProxy::setInstance($translator); } /** * Run application * * @return void + * @throws CacheException */ public function run(): void { @@ -299,6 +396,7 @@ public function run(): void * - Because it deprecated ({@link http://tools.ietf.org/html/rfc6648}) * * @return void + * @throws CacheException */ public function process(): void { @@ -317,17 +415,25 @@ public function process(): void */ protected function preProcess(): void { - Router::process(); + // analyse request and find required route + RouterProxy::process(); + + // rewrite request params with route data + RequestProxy::reset( + RouterProxy::getDefaultModule(), + RouterProxy::getDefaultController(), + RouterProxy::getParams() + ); // disable Layout for XmlHttpRequests - if (Request::isXmlHttpRequest()) { - $this->layoutFlag = false; + if (RequestProxy::isXmlHttpRequest()) { + $this->disableLayout(); } // switch to JSON response based on Accept header - if (Request::checkAccept([Request::TYPE_HTML, Request::TYPE_JSON]) === Request::TYPE_JSON) { - $this->layoutFlag = false; - Response::setType('JSON'); + if (RequestProxy::checkAccept(MimeType::JSON)) { + $this->disableLayout(); + ResponseProxy::setContentType(ResponseType::JSON); } } @@ -343,10 +449,9 @@ protected function preProcess(): void */ protected function doProcess(): void { - $module = Request::getModule(); - $controller = Request::getController(); - $params = Request::getParams(); - + $module = RequestProxy::getModule(); + $controller = RequestProxy::getController(); + $params = RequestProxy::getParams(); try { // try to dispatch controller $result = $this->dispatch($module, $controller, $params); @@ -362,13 +467,13 @@ protected function doProcess(): void } // setup layout, if needed - if ($this->useLayout()) { + if ($this->hasLayout()) { // render view to layout // needed for headScript and headStyle helpers - Layout::setContent($result->render()); - Response::setBody(Layout::getInstance()); + LayoutProxy::setContent($result->render()); + ResponseProxy::setBody(LayoutProxy::getInstance()); } else { - Response::setBody($result); + ResponseProxy::setBody($result); } } @@ -390,7 +495,7 @@ protected function postProcess(): void * * @param string $module * @param string $controller - * @param array $params + * @param array $params * * @return Controller * @throws CommonException @@ -400,15 +505,15 @@ protected function postProcess(): void */ public function dispatch(string $module, string $controller, array $params = []): Controller { - $instance = new Controller($module, $controller, $params); + $instance = new Controller($this->getPath(), $module, $controller, $params); - Logger::info("app:dispatch:>>>: $module/$controller"); + LoggerProxy::info("app:dispatch:>>>: $module/$controller"); $this->preDispatch($instance); - Logger::info("app:dispatch:===: $module/$controller"); + LoggerProxy::info("app:dispatch:===: $module/$controller"); $this->doDispatch($instance); - Logger::info("app:dispatch:<<<: $module/$controller"); + LoggerProxy::info("app:dispatch:<<<: $module/$controller"); $this->postDispatch($instance); return $instance; @@ -420,17 +525,98 @@ public function dispatch(string $module, string $controller, array $params = []) * @param Controller $controller * * @return void + * + * @throws ForbiddenException + * @throws NotAllowedException + * @throws NotAcceptableException */ protected function preDispatch(Controller $controller): void { // check HTTP method - $controller->checkHttpMethod(); + $this->checkHttpMethod($controller); // check ACL privileges - $controller->checkPrivilege(); + $this->checkPrivilege($controller); // check HTTP Accept header - $controller->checkHttpAccept(); + $this->checkHttpAccept($controller); + } + + /** + * @throws NotAllowedException + */ + protected function checkHttpMethod($controller): void + { + $methods = $this->getData()->getAttribute( + $controller->getModule(), + $controller->getController(), + 'method' + ); + + if (empty($methods) || in_array(RequestProxy::getMethod(), $methods, true)) { + return; + } + + // prepare list of the allowed methods + $methods = array_map(function ($method) { + return $method->value; + }, $methods); + throw new NotAllowedException(implode(',', $methods)); + } + + /** + * @throws ForbiddenException + */ + protected function checkPrivilege($controller): void + { + $privileges = $this->getData()->getAttribute( + $controller->getModule(), + $controller->getController(), + 'privilege' + ); + + if (empty($privileges)) { + return; + } + + $privileges = array_map('strtolower', $privileges); + $privileges = array_unique($privileges); + + foreach ($privileges as $privilege) { + if (AclProxy::isAllowed($controller->getModule(), $privilege)) { + return; + } + } + throw new ForbiddenException(); + } + + /** + * @throws NotAcceptableException + */ + protected function checkHttpAccept($controller): void + { + $acceptTypes = $this->getData()->getAttribute( + $controller->getModule(), + $controller->getController(), + 'accept' + ); + + // check accept header only if you need this + // mime type "ANY" allow anything + if (empty($acceptTypes) || in_array(MimeType::ANY, $acceptTypes)) { + return; + } + + $acceptRequest = RequestProxy::getAccept(); + + // try to search allowed types + foreach ($acceptRequest as $type => $q) { + $mimeType = MimeType::tryFrom($type); + if ($mimeType && in_array($mimeType, $acceptTypes)) { + return; + } + } + throw new NotAcceptableException(); } /** @@ -439,14 +625,45 @@ protected function preDispatch(Controller $controller): void * @param Controller $controller * * @return void + * @throws ReflectionException * @throws ComponentException * @throws ControllerException - * @throws ReflectionException */ protected function doDispatch(Controller $controller): void { - // run controller - $controller->run(); + $cacheKey = "data." . + $controller->getModule() . "." . + $controller->getController() . "." . + md5(http_build_query($controller->getParams())); + + $cacheTtl = $this->getData()->getAttribute( + $controller->getModule(), + $controller->getController(), + 'cache' + ); + + if ($cacheTtl && $data = CacheProxy::get($cacheKey)) { + $controller->setData($data); + } else { + if ( + $types = $this->getData()->getParams( + $controller->getModule(), + $controller->getController() + ) + ) { + $controller->setTypes($types); + } + + $controller->process(); + if ($cacheTtl) { + CacheProxy::set( + $cacheKey, + $controller->getData(), + $cacheTtl, + ['system', 'data', $controller->getModule() . ':' . $controller->getController()] + ); + } + } } /** @@ -468,7 +685,7 @@ protected function postDispatch(Controller $controller): void */ public function render(): void { - Response::send(); + ResponseProxy::send(); } /** diff --git a/src/Application/Exception/ApplicationException.php b/src/Application/ApplicationException.php similarity index 90% rename from src/Application/Exception/ApplicationException.php rename to src/Application/ApplicationException.php index 3ff25004..309632b8 100644 --- a/src/Application/Exception/ApplicationException.php +++ b/src/Application/ApplicationException.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Bluz\Application\Exception; +namespace Bluz\Application; use Bluz\Common\Exception\CommonException; diff --git a/src/Application/Data.php b/src/Application/Data.php new file mode 100644 index 00000000..adba79f5 --- /dev/null +++ b/src/Application/Data.php @@ -0,0 +1,188 @@ +path . '/modules/*/controllers/*.php'; + foreach (new GlobIterator($path) as $file) { + /* @var SplFileInfo $file */ + $module = $file->getPathInfo()->getPathInfo()->getBasename(); + $controller = $file->getBasename('.php'); + + /** + * @var Closure $controllerClosure + */ + $closure = include $file; + + if (!is_callable($closure)) { + throw new ApplicationException("Controller is not callable '{$module}/{$controller}'"); + } + + $this->container['modules'][$module][$controller] = []; + + $reflection = new \ReflectionFunction($closure); + + $parameters = $reflection->getParameters(); + foreach ($parameters as $parameter) { + $this->container['modules'][$module][$controller]['params'][$parameter->getName()] = + $parameter->getType()?->getName() ?? 'mixed'; + } + + $attributes = $reflection->getAttributes(); + foreach ($attributes as $attribute) { + $name = strtolower(substr(strrchr($attribute->getName(), '\\'), 1)); + $argument = current($attribute->getArguments()); + + if ($name === 'route') { + $this->prepareRoute( + route: $argument, + module: $module, + controller: $controller, + params: $this->container['modules'][$module][$controller]['params'] ?? [] + ); + } + $this->setAttribute($module, $controller, $name, $argument); + } + } + } + + /** + * @param string $module + * @param string $controller + * @param string $attribute + * @param mixed $value + * @return void + */ + protected function setAttribute(string $module, string $controller, string $attribute, mixed $value): void + { + Collection::add($this->container, 'modules', $module, $controller, '@', $attribute, $value); + } + + /** + * @param string $module + * @param string $controller + * @param string $attribute + * @return mixed|null + */ + public function getAttribute(string $module, string $controller, string $attribute): mixed + { + return Collection::get($this->container, 'modules', $module, $controller, '@', $attribute); + } + + /** + * @param string $module + * @param string $controller + * @return mixed|null + */ + public function getParams(string $module, string $controller): mixed + { + return Collection::get($this->container, 'modules', $module, $controller, 'params'); + } + + /** + * @param string $module + * @param string $controller + * @return mixed|null + */ + public function getRoute(string $module, string $controller): mixed + { + return $this->getAttribute($module, $controller, 'route'); + } + + /** + * @return array|null + */ + public function getRoutes(): ?array + { + return $this->get('routes'); + } + + /** + * @return array|null + */ + public function getModules(): ?array + { + return $this->get('modules'); + } + + /** + * @param string $route + * @param string $module + * @param string $controller + * @param array $params + * @return void + */ + protected function prepareRoute(string $route, string $module, string $controller, array $params = []): void + { + $pattern = $this->prepareRoutePattern($route, $params); + $this->container['routes'][$pattern] = [ + 'module' => $module, + 'controller' => $controller, + 'params' => $params + ]; + } + + /** + * Prepare Route pattern + * + * @param string $route + * @param array $params + * @return string + */ + protected function prepareRoutePattern(string $route, array $params): string + { + $pattern = str_replace('/', '\/', $route); + + foreach ($params as $param => $type) { + $replace = match ($type) { + 'int' => "(?P<$param>[0-9]+)", + 'float' => "(?P<$param>[0-9.,]+)", + default => "(?P<$param>[a-zA-Z0-9-_.]+)", // string, array, mixed, etc + }; + $pattern = str_replace("{\$$param}", $replace, $pattern); + } + return '/^' . $pattern . '/i'; + } +} diff --git a/src/Application/Helper/Error.php b/src/Application/Helper/Error.php index 283828b1..61a98da3 100644 --- a/src/Application/Helper/Error.php +++ b/src/Application/Helper/Error.php @@ -16,6 +16,7 @@ use Bluz\Common\Exception\ComponentException; use Bluz\Controller\Controller; use Bluz\Controller\ControllerException; +use Bluz\Http\Exception\HttpException; use Bluz\Proxy\Response; use Bluz\Proxy\Router; use Exception; @@ -42,7 +43,14 @@ function (Exception $exception) { $module = Router::getErrorModule(); $controller = Router::getErrorController(); - $params = ['code' => $exception->getCode(), 'exception' => $exception]; + + if ($exception instanceof HttpException) { + $code = $exception->getStatusCode()->value; + } else { + $code = $exception->getCode(); + } + + $params = ['code' => $code, 'exception' => $exception]; return $this->dispatch($module, $controller, $params); }; diff --git a/src/Application/Helper/Redirect.php b/src/Application/Helper/Redirect.php index 470410b4..438fa22e 100644 --- a/src/Application/Helper/Redirect.php +++ b/src/Application/Helper/Redirect.php @@ -29,7 +29,7 @@ function (RedirectException $exception) { /** * @var Application $this */ - $this->useLayout(false); + $this->disableLayout(); Response::removeHeaders(); Response::clearBody(); diff --git a/src/Auth/AbstractIdentity.php b/src/Auth/AbstractIdentity.php index 9397fd5f..9a6d6d5b 100644 --- a/src/Auth/AbstractIdentity.php +++ b/src/Auth/AbstractIdentity.php @@ -19,28 +19,28 @@ * @package Bluz\Auth * * @property integer $id - * @property string $login - * @property string $email + * @property string $login + * @property string $email */ abstract class AbstractIdentity extends Row implements IdentityInterface { /** - * {@inheritdoc} + * @inheritDoc */ abstract public function getPrivileges(): array; /** - * {@inheritdoc} + * @inheritDoc */ public function getId(): ?int { - return $this->id ? (int)$this->id : null; + return $this->id; } /** - * {@inheritdoc} + * @inheritDoc */ - public function hasPrivilege($module, $privilege): bool + public function hasPrivilege(string $module, string $privilege): bool { $privileges = $this->getPrivileges(); diff --git a/src/Auth/Auth.php b/src/Auth/Auth.php index 20bc266d..509b7b35 100644 --- a/src/Auth/Auth.php +++ b/src/Auth/Auth.php @@ -27,14 +27,14 @@ class Auth use Options; /** - * @var IdentityInterface Instance of EntityInterface + * @var ?IdentityInterface Instance of EntityInterface */ - protected $identity; + protected ?IdentityInterface $identity = null; /** * Setup identity * - * @param IdentityInterface $identity + * @param IdentityInterface $identity * * @return void */ diff --git a/src/Auth/IdentityInterface.php b/src/Auth/IdentityInterface.php index 0fb0b574..f7658664 100644 --- a/src/Auth/IdentityInterface.php +++ b/src/Auth/IdentityInterface.php @@ -21,7 +21,7 @@ interface IdentityInterface /** * Get an ID that can uniquely identify a user * - * @return integer + * @return int|null */ public function getId(): ?int; @@ -35,10 +35,10 @@ public function getPrivileges(): array; /** * Has it privilege? * - * @param string $module - * @param string $privilege + * @param string $module + * @param string $privilege * * @return bool */ - public function hasPrivilege($module, $privilege): bool; + public function hasPrivilege(string $module, string $privilege): bool; } diff --git a/src/Auth/Model/AbstractRow.php b/src/Auth/Model/AbstractRow.php index 9895b79d..518b2a12 100644 --- a/src/Auth/Model/AbstractRow.php +++ b/src/Auth/Model/AbstractRow.php @@ -20,14 +20,14 @@ * @author Anton Shevchuk * * @property integer $userId - * @property string $provider - * @property string $foreignKey - * @property string $token - * @property string $tokenSecret - * @property string $tokenType - * @property string $created - * @property string $updated - * @property string $expired + * @property string $provider + * @property string $foreignKey + * @property string $token + * @property string $tokenSecret + * @property string $tokenType + * @property string $created + * @property string $updated + * @property string $expired */ abstract class AbstractRow extends Row { diff --git a/src/Auth/Model/AbstractTable.php b/src/Auth/Model/AbstractTable.php index 445ccd75..218498f9 100644 --- a/src/Auth/Model/AbstractTable.php +++ b/src/Auth/Model/AbstractTable.php @@ -34,7 +34,7 @@ abstract class AbstractTable extends Table /** * Providers * - equals - login + password - * - token - token with ttl + * - token - token with ttl * - cookie - cookie token with ttl */ public const PROVIDER_COOKIE = 'cookie'; @@ -48,12 +48,12 @@ abstract class AbstractTable extends Table /** * @var string Table */ - protected $name = 'auth'; + protected string $name = 'auth'; /** * @var array Primary key(s) */ - protected $primary = ['provider', 'foreignKey']; + protected array $primary = ['provider', 'foreignKey']; /** * Get AuthRow @@ -61,13 +61,13 @@ abstract class AbstractTable extends Table * @param string $provider * @param string $foreignKey * - * @return RowInterface + * @return RowInterface|null * @throws InvalidArgumentException * @throws DbException * @throws InvalidPrimaryKeyException */ - public static function getAuthRow(string $provider, string $foreignKey): ?RowInterface + public function getAuthRow(string $provider, string $foreignKey): ?RowInterface { - return static::findRow(['provider' => $provider, 'foreignKey' => $foreignKey]); + return $this->findRow(['provider' => $provider, 'foreignKey' => $foreignKey]); } } diff --git a/src/Common/Container/ArrayAccess.php b/src/Common/Container/ArrayAccess.php deleted file mode 100644 index 83cfa058..00000000 --- a/src/Common/Container/ArrayAccess.php +++ /dev/null @@ -1,79 +0,0 @@ -doSetContainer($offset, $value); - } - - /** - * Offset to retrieve - * - * @param mixed $offset - * - * @return mixed - */ - public function offsetGet($offset) - { - return $this->doGetContainer($offset); - } - - /** - * Whether a offset exists - * - * @param mixed $offset - * - * @return bool - */ - public function offsetExists($offset): bool - { - return $this->doContainsContainer($offset); - } - - /** - * Offset to unset - * - * @param mixed $offset - */ - public function offsetUnset($offset): void - { - $this->doDeleteContainer($offset); - } -} diff --git a/src/Common/Container/Container.php b/src/Common/Container/Container.php deleted file mode 100644 index 880805bc..00000000 --- a/src/Common/Container/Container.php +++ /dev/null @@ -1,115 +0,0 @@ -container[$key] = $value; - } - - /** - * Get value by key - * - * @param string $key - * - * @return mixed - */ - protected function doGetContainer(string $key) - { - if ($this->doContainsContainer($key)) { - return $this->container[$key]; - } - return null; - } - - /** - * Check contains key in container - * - * @param string $key - * - * @return bool - */ - protected function doContainsContainer(string $key): bool - { - return array_key_exists($key, $this->container); - } - - /** - * Delete value by key - * - * @param string $key - * - * @return void - */ - protected function doDeleteContainer(string $key): void - { - unset($this->container[$key]); - } - - /** - * Sets all data in the row from an array - * - * @param array $data - * - * @return void - */ - public function setFromArray(array $data): void - { - foreach ($data as $key => $value) { - $this->container[$key] = $value; - } - } - - /** - * Returns the column/value data as an array - * - * @return array - */ - public function toArray(): array - { - return $this->container; - } - - /** - * Reset container array - * - * @return void - */ - public function resetArray(): void - { - foreach ($this->container as &$value) { - $value = null; - } - } -} diff --git a/src/Common/Container/JsonSerialize.php b/src/Common/Container/JsonSerialize.php deleted file mode 100644 index 84ffcee1..00000000 --- a/src/Common/Container/JsonSerialize.php +++ /dev/null @@ -1,32 +0,0 @@ -toArray(); - } -} diff --git a/src/Common/Container/MagicAccess.php b/src/Common/Container/MagicAccess.php deleted file mode 100644 index 8245ac5d..00000000 --- a/src/Common/Container/MagicAccess.php +++ /dev/null @@ -1,75 +0,0 @@ -doSetContainer($key, $value); - } - - /** - * Magic alias for get() regular method - * - * @param string $key - * - * @return mixed - */ - public function __get(string $key) - { - return $this->doGetContainer($key); - } - - /** - * Magic alias for contains() regular method - * - * @param string $key - * - * @return bool - */ - public function __isset(string $key): bool - { - return $this->doContainsContainer($key); - } - - /** - * Magic alias for delete() regular method - * - * @param string $key - * - * @return void - */ - public function __unset(string $key): void - { - $this->doDeleteContainer($key); - } -} diff --git a/src/Common/Container/RegularAccess.php b/src/Common/Container/RegularAccess.php deleted file mode 100644 index 74af75f3..00000000 --- a/src/Common/Container/RegularAccess.php +++ /dev/null @@ -1,75 +0,0 @@ -doSetContainer($key, $value); - } - - /** - * Get value by key - * - * @param string $key - * - * @return mixed - */ - public function get($key) - { - return $this->doGetContainer($key); - } - - /** - * Check contains key in container - * - * @param string $key - * - * @return bool - */ - public function contains($key): bool - { - return $this->doContainsContainer($key); - } - - /** - * Delete value by key - * - * @param string $key - * - * @return void - */ - public function delete($key): void - { - $this->doDeleteContainer($key); - } -} diff --git a/src/Common/Exception/CommonException.php b/src/Common/Exception/CommonException.php index d77a9971..38955b57 100644 --- a/src/Common/Exception/CommonException.php +++ b/src/Common/Exception/CommonException.php @@ -11,18 +11,12 @@ namespace Bluz\Common\Exception; -use Bluz\Http\StatusCode; - /** - * Basic Exception for Bluz framework + * Basic Exception for the framework * * @package Bluz\Common\Exception * @author Anton Shevchuk */ class CommonException extends \Exception { - /** - * @var integer Used as default HTTP code for exceptions - */ - protected $code = StatusCode::INTERNAL_SERVER_ERROR; } diff --git a/src/Validator/Exception/ComponentException.php b/src/Common/Exception/InitializationException.php similarity index 50% rename from src/Validator/Exception/ComponentException.php rename to src/Common/Exception/InitializationException.php index 229f5953..b6f1cd2b 100644 --- a/src/Validator/Exception/ComponentException.php +++ b/src/Common/Exception/InitializationException.php @@ -9,16 +9,14 @@ declare(strict_types=1); -namespace Bluz\Validator\Exception; - -use Bluz\Common\Exception; +namespace Bluz\Common\Exception; /** - * Component Exception + * Exception throws when class has wrong initialization options * - * @package Bluz\Validator\Exception + * @package Bluz\Common\Exception * @author Anton Shevchuk */ -class ComponentException extends Exception\ComponentException +class InitializationException extends ComponentException { } diff --git a/src/Common/Helper.php b/src/Common/Helper.php index a9dcc444..67e330aa 100644 --- a/src/Common/Helper.php +++ b/src/Common/Helper.php @@ -26,17 +26,17 @@ trait Helper /** * @var array[] list of helpers */ - protected static $helpers = []; + protected static array $helpers = []; /** * @var array[] list of helpers paths */ - protected static $helpersPath = []; + protected static array $helpersPath = []; /** * Add helper path * - * @param string $path + * @param string $path * * @return void * @throws CommonException @@ -51,9 +51,7 @@ public function addHelperPath(string $path): void } // create store of helpers - if (!isset(static::$helpersPath[$class])) { - static::$helpersPath[$class] = []; - } + static::$helpersPath[$class] ??= []; if (!in_array($realPath, static::$helpersPath[$class], true)) { static::$helpersPath[$class][] = $realPath; @@ -63,8 +61,8 @@ public function addHelperPath(string $path): void /** * Call magic helper * - * @param string $method - * @param array $arguments + * @param string $method + * @param array $arguments * * @return mixed * @throws CommonException @@ -114,8 +112,8 @@ private function loadHelper(string $name): void /** * Add helper callable * - * @param string $name - * @param string $path + * @param string $name + * @param string $path * * @return void * @throws CommonException @@ -124,10 +122,8 @@ private function addHelper(string $name, string $path): void { $class = static::class; - // create store of helpers for this class - if (!isset(static::$helpers[$class])) { - static::$helpers[$class] = []; - } + // create a store of helpers for this class + static::$helpers[$class] ??= []; $helper = include $path; diff --git a/src/Common/Instance.php b/src/Common/Instance.php index 594d3103..1e96da88 100644 --- a/src/Common/Instance.php +++ b/src/Common/Instance.php @@ -19,16 +19,15 @@ */ trait Instance { + protected static array $instances = []; + /** * Get instance * @return static */ - public static function getInstance() + public static function getInstance(): static { - static $instance; - if (null === $instance) { - $instance = new static(); - } - return $instance; + static::$instances[static::class] ??= new static(); + return static::$instances[static::class]; } } diff --git a/src/Common/Nil.php b/src/Common/Nil.php index ba575e1a..690efbc7 100644 --- a/src/Common/Nil.php +++ b/src/Common/Nil.php @@ -26,8 +26,8 @@ class Nil /** * Magic call * - * @param string $method - * @param array $args + * @param string $method + * @param array $args * * @return null */ @@ -39,8 +39,8 @@ public function __call($method, $args) /** * Magic call for static * - * @param string $method - * @param array $args + * @param string $method + * @param array $args * * @return null */ @@ -52,7 +52,7 @@ public static function __callStatic($method, $args) /** * Magic __get * - * @param string $key + * @param string $key * * @return null */ @@ -64,8 +64,8 @@ public function __get($key) /** * Magic __set * - * @param string $key - * @param mixed $value + * @param string $key + * @param mixed $value * * @return null */ @@ -77,7 +77,7 @@ public function __set($key, $value) /** * Magic __isset * - * @param string $key + * @param string $key * * @return false */ diff --git a/src/Common/Options.php b/src/Common/Options.php index 1db0d85b..f02675f2 100644 --- a/src/Common/Options.php +++ b/src/Common/Options.php @@ -47,17 +47,17 @@ trait Options /** * @var array options store */ - protected $options = []; + protected array $options = []; /** * Get option by key * - * @param string $key - * @param array $keys + * @param string $key + * @param array $keys * * @return mixed */ - public function getOption(string $key, ...$keys) + public function getOption(string $key, ...$keys): mixed { $method = 'get' . Str::toCamelCase($key); if (method_exists($this, $method)) { @@ -69,12 +69,12 @@ public function getOption(string $key, ...$keys) /** * Set option by key over setter * - * @param string $key - * @param mixed $value + * @param string $key + * @param mixed $value * * @return void */ - public function setOption(string $key, $value): void + public function setOption(string $key, mixed $value): void { $method = 'set' . Str::toCamelCase($key); if (method_exists($this, $method)) { @@ -98,7 +98,7 @@ public function getOptions(): array * Setup, check and init options * * Requirements - * - options must be a array + * - options must be an array * - options can be null * * @param array|null $options diff --git a/src/Config/Config.php b/src/Config/Config.php index 2531824c..57fb4cb2 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -12,8 +12,8 @@ namespace Bluz\Config; use Bluz\Collection\Collection; -use Bluz\Common\Container\Container; -use Bluz\Common\Container\RegularAccess; +use Bluz\Container\Container; +use Bluz\Container\RegularAccess; /** * Config @@ -35,7 +35,7 @@ class Config * @return array|mixed * @throws ConfigException */ - public function get(...$keys) + public function get(...$keys): mixed { // configuration is missed if (empty($this->container)) { diff --git a/src/Config/ConfigLoader.php b/src/Config/ConfigLoader.php index af16bf0c..66f74cca 100644 --- a/src/Config/ConfigLoader.php +++ b/src/Config/ConfigLoader.php @@ -26,17 +26,7 @@ class ConfigLoader /** * @var array configuration data */ - protected $config; - - /** - * @var string path to configuration files - */ - protected $path; - - /** - * @var string environment - */ - protected $environment; + protected array $config = []; /** * @return array @@ -47,67 +37,19 @@ public function getConfig(): array } /** - * @return string - */ - public function getPath(): string - { - return $this->path; - } - - /** - * Set path to configuration files + * Load and merge configuration * * @param string $path - * * @return void * @throws ConfigException */ - public function setPath(string $path): void + public function load(string $path): void { if (!is_dir($path)) { throw new ConfigException('Configuration directory is not exists'); } - $this->path = rtrim($path, '/'); - } - /** - * @return string - */ - public function getEnvironment(): string - { - return $this->environment; - } - - /** - * Set application environment - * - * @param string $environment - * - * @return void - */ - public function setEnvironment(string $environment): void - { - $this->environment = $environment; - } - - /** - * Load configuration - * - * @return void - * @throws ConfigException - */ - public function load(): void - { - if (!$this->path) { - throw new ConfigException('Configuration directory is not setup'); - } - - $this->config = $this->loadFiles($this->path . '/configs/default'); - - if ($this->environment) { - $customConfig = $this->loadFiles($this->path . '/configs/' . $this->environment); - $this->config = array_replace_recursive($this->config, $customConfig); - } + $this->config = array_replace_recursive($this->config, $this->loadFiles($path)); } /** @@ -118,9 +60,9 @@ public function load(): void * @return mixed * @throws ConfigException */ - protected function loadFile(string $path) + protected function loadFile(string $path): mixed { - if (!is_file($path) && !is_readable($path)) { + if (!is_file($path) || !is_readable($path)) { throw new ConfigException("Configuration file `$path` not found"); } return include $path; @@ -147,6 +89,7 @@ protected function loadFiles(string $path): array FilesystemIterator::KEY_AS_FILENAME | FilesystemIterator::CURRENT_AS_PATHNAME ); + foreach ($iterator as $name => $file) { $name = substr($name, 0, -4); $config[$name] = $this->loadFile($file); diff --git a/src/Controller/Attribute/Accept.php b/src/Controller/Attribute/Accept.php new file mode 100644 index 00000000..82a79ac2 --- /dev/null +++ b/src/Controller/Attribute/Accept.php @@ -0,0 +1,24 @@ +addHelperPath(__DIR__ . '/Helper/'); - $this->setModule($module); - $this->setController($controller); - $this->setParams($params); - $this->setTemplate($controller . '.phtml'); + $this->path = $path; + $this->module = $module; + $this->controller = $controller; + $this->params = $params; - $this->key = "data.$module.$controller." . md5(http_build_query($params)); + $this->setTemplate($controller . '.phtml'); + $this->findFile(); } /** @@ -123,14 +122,6 @@ public function getModule(): string return $this->module; } - /** - * @param string $module - */ - protected function setModule(string $module): void - { - $this->module = $module; - } - /** * @return string */ @@ -140,31 +131,31 @@ public function getController(): string } /** - * @param string $controller + * @return array */ - protected function setController(string $controller): void + public function getParams(): array { - $this->controller = $controller; + return $this->params; } /** * @return array */ - public function getParams(): array + public function getTypes(): array { - return $this->params; + return $this->types; } /** - * @param array $params + * @param array $types */ - protected function setParams(array $params): void + public function setTypes(array $types): void { - $this->params = $params; + $this->types = $types; } /** - * @return string + * @return string|null */ public function getTemplate(): ?string { @@ -179,23 +170,6 @@ protected function setTemplate(string $template): void $this->template = $template; } - /** - * Run controller logic - * - * @return Data - * @throws ComponentException - * @throws ControllerException - * @throws ReflectionException - */ - public function run(): Data - { - if (!$this->loadData()) { - $this->process(); - $this->saveData(); - } - return $this->data; - } - /** * Controller run * @@ -204,7 +178,7 @@ public function run(): Data * @throws ControllerException * @throws ReflectionException */ - protected function process(): Data + public function process(): Data { // initial variables for use inside controller $module = $this->module; @@ -221,7 +195,7 @@ protected function process(): Data } // process params - $params = $this->getMeta()->params($params); + $params = $this->params(); // call Closure or Controller $result = $controllerClosure(...$params); @@ -250,6 +224,34 @@ protected function process(): Data return $this->getData(); } + /** + * Process request params + * + * - type conversion + * - set default value + * + * @return array + */ + protected function params(): array + { + // apply type and default value for request params + $params = []; + foreach ($this->types as $param => $type) { + if (isset($this->params[$param])) { + $params[] = match ($type) { + 'bool' => (bool)$this->params[$param], + 'int' => (int)$this->params[$param], + 'float' => (float)$this->params[$param], + 'string' => (string)$this->params[$param], + 'array' => (array)$this->params[$param], + default => $this->params[$param], + }; + } + } + return $params; + } + + /** * Setup controller file * @@ -258,8 +260,7 @@ protected function process(): Data */ protected function findFile(): void { - $path = Application::getInstance()->getPath(); - $file = "$path/modules/{$this->module}/controllers/{$this->controller}.php"; + $file = "{$this->path}/modules/{$this->module}/controllers/{$this->controller}.php"; if (!file_exists($file)) { throw new ControllerException("Controller file not found '{$this->module}/{$this->controller}'", 404); @@ -272,68 +273,21 @@ protected function findFile(): void * Get controller file path * * @return string - * @throws ControllerException */ protected function getFile(): string { - if (!$this->file) { - $this->findFile(); - } return $this->file; } - /** - * Retrieve reflection for anonymous function - * - * @return void - * @throws ComponentException - * @throws ControllerException - * @throws ReflectionException - */ - protected function initMeta(): void - { - // cache for reflection data - $cacheKey = "meta.{$this->module}.{$this->controller}"; - - if (!$meta = Cache::get($cacheKey)) { - $meta = new Meta($this->getFile()); - $meta->process(); - - Cache::set( - $cacheKey, - $meta, - Cache::TTL_NO_EXPIRY, - ['system', 'meta'] - ); - } - $this->meta = $meta; - } - - /** - * Get meta information - * - * @return Meta - * @throws ControllerException - * @throws ComponentException - * @throws ReflectionException - */ - public function getMeta(): Meta - { - if (!$this->meta) { - $this->initMeta(); - } - return $this->meta; - } - /** * Assign key/value pair to Data object * - * @param string $key - * @param mixed $value + * @param string $key + * @param mixed $value * * @return void */ - public function assign(string $key, $value): void + public function assign(string $key, mixed $value): void { $this->getData()->set($key, $value); } @@ -352,43 +306,14 @@ public function getData(): Data } /** - * Load Data from cache + * Set controller Data container * - * @return bool - * @throws ComponentException - * @throws ControllerException - * @throws ReflectionException - */ - private function loadData(): bool - { - $cacheTime = $this->getMeta()->getCache(); - - if ($cacheTime && $cached = Cache::get($this->key)) { - $this->data = $cached; - return true; - } - return false; - } - - /** - * Save Data to cache - * - * @return bool - * @throws ComponentException - * @throws ControllerException - * @throws ReflectionException + * @param Data $data + * @return void */ - private function saveData(): bool + public function setData(Data $data): void { - if ($cacheTime = $this->getMeta()->getCache()) { - return Cache::set( - $this->key, - $this->getData(), - $cacheTime, - ['system', 'data'] - ); - } - return false; + $this->data = $data; } /** @@ -396,7 +321,7 @@ private function saveData(): bool * * @return Data */ - public function jsonSerialize() + public function jsonSerialize(): mixed { return $this->getData(); } @@ -416,22 +341,21 @@ public function __toString() // $view for use in closure $view = new View(); - $path = Application::getInstance()->getPath(); - // setup additional helper path - $view->addHelperPath($path . '/layouts/helpers'); + $view->addHelperPath($this->path . '/layouts/helpers'); // setup additional partial path - $view->addPartialPath($path . '/layouts/partial'); + $view->addPartialPath($this->path . '/layouts/partial'); // setup default path - $view->setPath($path . '/modules/' . $this->module . '/views'); + $view->setPath($this->path . '/modules/' . $this->module . '/views'); // setup template $view->setTemplate($this->template); // setup data $view->setFromArray($this->getData()->toArray()); + return $view->render(); } catch (Exception $e) { // save log diff --git a/src/Controller/ControllerException.php b/src/Controller/ControllerException.php index 16175b40..79c5409f 100644 --- a/src/Controller/ControllerException.php +++ b/src/Controller/ControllerException.php @@ -11,7 +11,7 @@ namespace Bluz\Controller; -use Bluz\Application\Exception\ApplicationException; +use Bluz\Application\ApplicationException; /** * ControllerException diff --git a/src/Controller/Data.php b/src/Controller/Data.php index d377bbed..e053d535 100644 --- a/src/Controller/Data.php +++ b/src/Controller/Data.php @@ -11,11 +11,11 @@ namespace Bluz\Controller; -use Bluz\Common\Container\ArrayAccess; -use Bluz\Common\Container\Container; -use Bluz\Common\Container\JsonSerialize; -use Bluz\Common\Container\MagicAccess; -use Bluz\Common\Container\RegularAccess; +use Bluz\Container\ArrayAccess; +use Bluz\Container\Container; +use Bluz\Container\JsonSerialize; +use Bluz\Container\MagicAccess; +use Bluz\Container\RegularAccess; /** * Data diff --git a/src/Controller/Helper/Attachment.php b/src/Controller/Helper/Attachment.php index 54358dd3..28892cbc 100644 --- a/src/Controller/Helper/Attachment.php +++ b/src/Controller/Helper/Attachment.php @@ -13,6 +13,7 @@ use Bluz\Controller\Controller; use Bluz\Proxy\Response; +use Bluz\Response\ResponseType; /** * Switch layout @@ -24,7 +25,7 @@ function ($file) { /** * @var Controller $this */ - Response::setType('FILE'); + Response::setContentType(ResponseType::FILE); $this->assign('FILE', $file); $this->disableLayout(); $this->disableView(); diff --git a/src/Controller/Helper/CheckHttpAccept.php b/src/Controller/Helper/CheckHttpAccept.php deleted file mode 100644 index e44c3fe2..00000000 --- a/src/Controller/Helper/CheckHttpAccept.php +++ /dev/null @@ -1,69 +0,0 @@ -getMeta()->getAccept(); - - // some controllers hasn't @accept tag - if (!$allowAccept) { - // but by default allow just HTML output - $allowAccept = [Request::TYPE_HTML, Request::TYPE_ANY]; - } - - // get Accept with high priority - $accept = Request::checkAccept($allowAccept); - - // some controllers allow any type (*/*) - // and client doesn't send Accept header - if (!$accept && in_array(Request::TYPE_ANY, $allowAccept, true)) { - // all OK, controller should realize logic for response - return; - } - - // some controllers allow just selected types - // choose MIME type by browser accept header - // filtered by controller @accept - // switch statement for this logic - switch ($accept) { - case Request::TYPE_ANY: - case Request::TYPE_HTML: - // HTML response with layout - break; - case Request::TYPE_JSON: - // JSON response - $this->disableView(); - break; - default: - throw new NotAcceptableException(); - } - }; diff --git a/src/Controller/Helper/CheckHttpMethod.php b/src/Controller/Helper/CheckHttpMethod.php deleted file mode 100644 index c2ee3d41..00000000 --- a/src/Controller/Helper/CheckHttpMethod.php +++ /dev/null @@ -1,39 +0,0 @@ -getMeta()->getMethod(); - if ($methods && !in_array(Request::getMethod(), $methods, true)) { - throw new NotAllowedException(implode(',', $methods)); - } - }; diff --git a/src/Controller/Helper/CheckPrivilege.php b/src/Controller/Helper/CheckPrivilege.php deleted file mode 100644 index 084aedf5..00000000 --- a/src/Controller/Helper/CheckPrivilege.php +++ /dev/null @@ -1,39 +0,0 @@ -getMeta()->getPrivilege(); - if ($privilege && !Acl::isAllowed($this->getModule(), $privilege)) { - throw new ForbiddenException(); - } - }; diff --git a/src/Controller/Helper/DisableLayout.php b/src/Controller/Helper/DisableLayout.php index 71e93147..e71da5aa 100644 --- a/src/Controller/Helper/DisableLayout.php +++ b/src/Controller/Helper/DisableLayout.php @@ -11,8 +11,8 @@ namespace Bluz\Controller\Helper; -use Bluz\Application\Application; use Bluz\Controller\Controller; +use Bluz\Proxy\Application; /** * Switch layout or disable it @@ -24,5 +24,5 @@ function () { /** * @var Controller $this */ - Application::getInstance()->useLayout(false); + Application::getInstance()->disableLayout(); }; diff --git a/src/Controller/Helper/Dispatch.php b/src/Controller/Helper/Dispatch.php index c506dea5..d13a575e 100644 --- a/src/Controller/Helper/Dispatch.php +++ b/src/Controller/Helper/Dispatch.php @@ -11,11 +11,11 @@ namespace Bluz\Controller\Helper; -use Bluz\Application\Application; use Bluz\Common\Exception\CommonException; use Bluz\Common\Exception\ComponentException; use Bluz\Controller\Controller; use Bluz\Controller\ControllerException; +use Bluz\Proxy\Application; use ReflectionException; /** diff --git a/src/Controller/Helper/UseJson.php b/src/Controller/Helper/UseJson.php index a3b0d260..a4cd75f8 100644 --- a/src/Controller/Helper/UseJson.php +++ b/src/Controller/Helper/UseJson.php @@ -11,9 +11,10 @@ namespace Bluz\Controller\Helper; -use Bluz\Application\Application; use Bluz\Controller\Controller; +use Bluz\Proxy\Application; use Bluz\Proxy\Response; +use Bluz\Response\ResponseType; /** * Switch to JSON content @@ -25,6 +26,6 @@ function () { /** * @var Controller $this */ - Application::getInstance()->useLayout(false); - Response::setType('JSON'); + Application::getInstance()->disableLayout(); + Response::setContentType(ResponseType::JSON); }; diff --git a/src/Controller/Helper/UseLayout.php b/src/Controller/Helper/UseLayout.php index 64b4b682..ff284988 100644 --- a/src/Controller/Helper/UseLayout.php +++ b/src/Controller/Helper/UseLayout.php @@ -11,8 +11,8 @@ namespace Bluz\Controller\Helper; -use Bluz\Application\Application; use Bluz\Controller\Controller; +use Bluz\Proxy\Application; use Bluz\Proxy\Layout; /** @@ -25,6 +25,6 @@ function (string $layout) { /** * @var Controller $this */ - Application::getInstance()->useLayout(true); + Application::getInstance()->enableLayout(); Layout::setTemplate($layout); }; diff --git a/src/Controller/Mapper/AbstractMapper.php b/src/Controller/Mapper/AbstractMapper.php index f0447c2a..08715177 100644 --- a/src/Controller/Mapper/AbstractMapper.php +++ b/src/Controller/Mapper/AbstractMapper.php @@ -11,7 +11,6 @@ namespace Bluz\Controller\Mapper; -use Bluz\Application\Application; use Bluz\Common\Exception\CommonException; use Bluz\Common\Exception\ComponentException; use Bluz\Controller\Controller; @@ -23,6 +22,7 @@ use Bluz\Http\Exception\NotImplementedException; use Bluz\Http\RequestMethod; use Bluz\Proxy\Acl; +use Bluz\Proxy\Application; use Bluz\Proxy\Request; use Bluz\Proxy\Router; use ReflectionException; @@ -36,49 +36,49 @@ abstract class AbstractMapper { /** - * @var string HTTP Method + * @var RequestMethod HTTP Method */ - protected $method = RequestMethod::GET; + protected RequestMethod $method = RequestMethod::GET; /** * @var string */ - protected $module; + protected string $module; /** * @var string */ - protected $controller; + protected string $controller; /** * @var array identifier */ - protected $primary; + protected array $primary; /** - * @var string relation list + * @var ?string relation list */ - protected $relation; + protected ?string $relation = null; /** - * @var string relation Id + * @var ?string relation ID */ - protected $relationId; + protected ?string $relationId = null; /** * @var array params of query */ - protected $params = []; + protected array $params = []; /** * @var array query data */ - protected $data = []; + protected array $data = []; /** * @var AbstractCrud instance of CRUD */ - protected $crud; + protected AbstractCrud $crud; /** * [ @@ -92,7 +92,7 @@ abstract class AbstractMapper * * @var Link[] */ - protected $map = []; + protected array $map = []; /** * Prepare params @@ -112,15 +112,15 @@ public function __construct(AbstractCrud $crud) /** * Add mapping data * - * @param string $method + * @param RequestMethod $method * @param string $module * @param string $controller * * @return Link */ - public function addMap(string $method, string $module, string $controller): Link + public function addMap(RequestMethod $method, string $module, string $controller): Link { - return $this->map[strtoupper($method)] = new Link($module, $controller); + return $this->map[$method->value] = new Link($module, $controller); } /** @@ -252,12 +252,11 @@ public function run(): Controller protected function prepareRequest(): void { // HTTP method - $method = Request::getMethod(); - $this->method = strtoupper($method); + $this->method = Request::getMethod(); // get path // %module% / %controller% / %id% / %relation% / %id% - $path = Router::getCleanUri(); + $path = Request::getPath(); $this->params = explode('/', rtrim($path, '/')); @@ -292,11 +291,11 @@ protected function prepareRequest(): void protected function dispatch() { // check implementation - if (!isset($this->map[$this->method])) { + if (!isset($this->map[$this->method->value])) { throw new NotImplementedException(); } - $link = $this->map[$this->method]; + $link = $this->map[$this->method->value]; // check permissions if (!Acl::isAllowed($this->module, $link->getAcl())) { @@ -306,12 +305,10 @@ protected function dispatch() $this->crud->setFields($link->getFields()); // dispatch controller - $result = Application::getInstance()->dispatch( + return Application::getInstance()->dispatch( $link->getModule(), $link->getController(), $this->prepareParams() ); - - return $result; } } diff --git a/src/Controller/Mapper/Link.php b/src/Controller/Mapper/Link.php index 9ba97d4b..cb61c28b 100644 --- a/src/Controller/Mapper/Link.php +++ b/src/Controller/Mapper/Link.php @@ -22,22 +22,22 @@ class Link /** * @var string */ - protected $module; + protected string $module; /** * @var string */ - protected $controller; + protected string $controller; /** - * @var string + * @var string|null */ - protected $acl; + protected ?string $acl = null; /** * @var array */ - protected $fields = []; + protected array $fields = []; /** * Constructor of Link @@ -78,7 +78,7 @@ public function fields(array $fields): Link } /** - * @return string + * @return string|null */ public function getModule(): ?string { @@ -94,7 +94,7 @@ protected function setModule(string $module): void } /** - * @return string + * @return string|null */ public function getController(): ?string { diff --git a/src/Controller/Meta.php b/src/Controller/Meta.php deleted file mode 100644 index e443312d..00000000 --- a/src/Controller/Meta.php +++ /dev/null @@ -1,451 +0,0 @@ -file = $file; - } - - /** - * Set state required for working with var_export (used inside PHP File cache) - * - * @param array $array - * - * @return Meta - */ - public static function __set_state($array) - { - $instance = new Meta($array['file']); - foreach ($array as $key => $value) { - $instance->{$key} = $value; - } - return $instance; - } - - /** - * Process to get reflection from file - * - * @return void - * @throws ComponentException - * @throws ReflectionException - */ - public function process(): void - { - /** @var Closure|object $closure */ - $closure = include $this->file; - - if (!is_callable($closure)) { - throw new ComponentException("There is no callable structure in file `{$this->file}`"); - } - - $reflection = new \ReflectionFunction($closure); - - // check and normalize params by doc comment - $docComment = $reflection->getDocComment(); - - // get all options by one regular expression - if (preg_match_all('/\s*\*\s*\@([a-z0-9-_]+)\s+(.*).*\s+/i', $docComment, $matches)) { - foreach ($matches[1] as $i => $key) { - $this->setOption($key, trim($matches[2][$i])); - } - } - - // init routes - $this->initRoute(); - - // get params and convert it to simple array - $reflectionParams = $reflection->getParameters(); - // setup params and optional params - foreach ($reflectionParams as $param) { - $name = $param->getName(); - // if some function params is missed in description - if (!isset($this->params[$name])) { - $this->params[$name] = null; - } - if ($param->isOptional()) { - $this->values[$name] = $param->getDefaultValue(); - } - } - } - - /** - * Process request params - * - * - type conversion - * - set default value - * - * @param array $requestParams - * - * @return array - */ - public function params(array $requestParams): array - { - // apply type and default value for request params - $params = []; - foreach ($this->params as $param => $type) { - if (isset($requestParams[$param])) { - switch ($type) { - case 'bool': - case 'boolean': - $params[] = (bool)$requestParams[$param]; - break; - case 'int': - case 'integer': - $params[] = (int)$requestParams[$param]; - break; - case 'float': - $params[] = (float)$requestParams[$param]; - break; - case 'string': - $params[] = (string)$requestParams[$param]; - break; - case 'array': - $params[] = (array)$requestParams[$param]; - break; - default: - $params[] = $requestParams[$param]; - break; - } - } elseif (isset($this->values[$param])) { - $params[] = $this->values[$param]; - } else { - $params[] = null; - } - } - return $params; - } - - /** - * Get path to file - * - * @return string - */ - public function getFile(): string - { - return $this->file; - } - - /** - * Get Cache TTL - * - * @return integer - */ - public function getCache(): int - { - return $this->cache; - } - - /** - * Set Cache TTL - * - * @param string $ttl - * - * @return void - */ - public function setCache(string $ttl): void - { - $this->cache = $this->prepareCache($ttl); - } - - /** - * Prepare Cache - * - * @param string $cache - * - * @return integer - */ - protected function prepareCache(string $cache): int - { - $num = (int)$cache; - $time = 'min'; - - if ($pos = strpos($cache, ' ')) { - $time = substr($cache, $pos); - } - - switch ($time) { - case 'day': - case 'days': - return $num * 86400; - case 'hour': - case 'hours': - return $num * 3600; - case 'min': - default: - return $num * 60; - } - } - - /** - * Get accepted type - * - * @return array|null - */ - public function getAccept(): ?array - { - return count($this->accept) ? $this->accept : null; - } - - /** - * Set accepted types - * - * @param string $accept - * - * @return void - */ - public function setAccept(string $accept): void - { - // allow accept map - $acceptMap = [ - 'ANY' => Request::TYPE_ANY, - 'HTML' => Request::TYPE_HTML, - 'JSON' => Request::TYPE_JSON - ]; - - $accept = strtoupper($accept); - - if (isset($acceptMap[$accept])) { - $this->accept[] = $acceptMap[$accept]; - } - } - - /** - * Get Acl privileges - * - * @return array|null - */ - public function getAcl(): ?array - { - return count($this->acl) ? $this->acl : null; - } - - /** - * Set Acl privileges - * - * @param string $acl - * - * @return void - */ - public function setAcl(string $acl): void - { - $this->acl[] = $acl; - } - - /** - * Get HTTP Method - * - * @return array|null - */ - public function getMethod(): ?array - { - return count($this->method) ? $this->method : null; - } - - /** - * Set HTTP Method - * - * @param string $method - * - * @return void - */ - public function setMethod(string $method): void - { - $this->method[] = strtoupper($method); - } - - /** - * Get all params - * - * @return array - */ - public function getParams(): array - { - return $this->params; - } - - /** - * Set param types - * - * @param string $param - * - * @return void - */ - public function setParam(string $param): void - { - // prepare params data - // setup param types - if (strpos($param, '$') === false) { - return; - } - - [$type, $key] = preg_split('/[ $]+/', $param); - - $this->params[$key] = trim($type); - } - - /** - * Get Privilege fo ACL - * - * @return string|null - */ - public function getPrivilege(): ?string - { - return $this->privilege; - } - - /** - * Set Privilege fo ACL allow only one privilege - * - * @param string $privilege - * - * @return void - */ - public function setPrivilege(string $privilege): void - { - $this->privilege = $privilege; - } - - /** - * Get Route - * - * @return array|null - */ - public function getRoute(): ?array - { - return count($this->route) ? $this->route : null; - } - - /** - * Set Route - * - * @param string $route - * - * @return void - */ - public function setRoute(string $route): void - { - $this->route[$route] = null; - } - - /** - * Init Route - * - * @return void - */ - protected function initRoute(): void - { - foreach ($this->route as $route => &$pattern) { - $pattern = $this->prepareRoutePattern($route); - } - } - - /** - * Prepare Route pattern - * - * @param string $route - * - * @return string - */ - protected function prepareRoutePattern(string $route): string - { - $pattern = str_replace('/', '\/', $route); - - foreach ($this->getParams() as $param => $type) { - switch ($type) { - case 'int': - case 'integer': - $pattern = str_replace("{\$$param}", "(?P<$param>[0-9]+)", $pattern); - break; - case 'float': - $pattern = str_replace("{\$$param}", "(?P<$param>[0-9.,]+)", $pattern); - break; - case 'string': - case 'module': - case 'controller': - $pattern = str_replace( - "{\$$param}", - "(?P<$param>[a-zA-Z0-9-_.]+)", - $pattern - ); - break; - } - } - return '/^' . $pattern . '/i'; - } -} diff --git a/src/Crud/AbstractCrud.php b/src/Crud/AbstractCrud.php deleted file mode 100644 index ba40b7b6..00000000 --- a/src/Crud/AbstractCrud.php +++ /dev/null @@ -1,173 +0,0 @@ -fields; - } - - /** - * @param array $fields - */ - public function setFields(array $fields): void - { - $this->fields = $fields; - } - - /** - * Filter input Fields - * - * @param array $data Request - * - * @return array - */ - protected function filterData($data): array - { - if (empty($this->getFields())) { - return $data; - } - return array_intersect_key($data, array_flip($this->getFields())); - } - - /** - * Filter output Row - * - * @param RowInterface $row from database - * - * @return RowInterface - */ - protected function filterRow(RowInterface $row): RowInterface - { - if (empty($this->getFields())) { - return $row; - } - $fields = array_keys($row->toArray()); - $toDelete = array_diff($fields, $this->getFields()); - - foreach ($toDelete as $field) { - unset($row->$field); - } - return $row; - } -} diff --git a/src/Crud/CrudException.php b/src/Crud/CrudException.php deleted file mode 100644 index 09273ab9..00000000 --- a/src/Crud/CrudException.php +++ /dev/null @@ -1,23 +0,0 @@ -getTable()->getPrimaryKey(); - } - - /** - * Get record from Db or create new object - * - * @param mixed $primary - * - * @return Db\RowInterface - * @throws TableNotFoundException - * @throws NotFoundException - */ - public function readOne($primary) - { - if (!$primary) { - return $this->getTable()::create(); - } - - $row = $this->getTable()::findRow($primary); - - if (!$row) { - throw new NotFoundException('Record not found'); - } - - return $this->filterRow($row); - } - - /** - * Get set of records - * - * @param int $offset - * @param int $limit - * @param array $params - * - * @return array[Row[], integer] - * @throws TableNotFoundException - */ - public function readSet(int $offset = 0, int $limit = 10, array $params = []) - { - $select = $this->getTable()::select(); - - // select only required fields - if (count($this->getFields())) { - $fields = $this->getFields(); - $name = $this->getTable()->getName(); - $fields = array_map( - function ($field) use ($name) { - return $name . '.' . $field; - }, - $fields - ); - $select->select(implode(', ', $fields)); - } - - // switch statement for DB type - $type = Proxy\Db::getOption('connect', 'type'); - switch ($type) { - case 'mysql': - $selectPart = $select->getSelect(); - $selectPart[0] = 'SQL_CALC_FOUND_ROWS ' . $selectPart[0]; - $select->select(...$selectPart); - $totalSQL = 'SELECT FOUND_ROWS()'; - break; - case 'pgsql': - default: - $selectTotal = clone $select; - $selectTotal->select('COUNT(*)'); - $totalSQL = $selectTotal->getSql(); - break; - } - - $select->setLimit($limit); - $select->setOffset($offset); - - $result = []; - $total = 0; - - // run queries - // use transaction to avoid errors - Proxy\Db::transaction( - function () use (&$result, &$total, $select, $totalSQL) { - $result = $select->execute(); - $total = Proxy\Db::fetchOne($totalSQL); - } - ); - - return [$result, $total]; - } - - /** - * Create item - * - * @param array $data - * - * @return mixed - * @throws TableNotFoundException - */ - public function createOne(array $data) - { - $row = $this->getTable()::create(); - - $data = $this->filterData($data); - - $row->setFromArray($data); - return $row->save(); - } - - /** - * Update item - * - * @param mixed $primary - * @param array $data - * - * @return integer - * @throws NotFoundException - * @throws TableNotFoundException - */ - public function updateOne($primary, array $data) - { - $row = $this->getTable()::findRow($primary); - - if (!$row) { - throw new NotFoundException('Record not found'); - } - $data = $this->filterData($data); - - $row->setFromArray($data); - return $row->save(); - } - - /** - * Delete item - * - * @param mixed $primary - * - * @return integer - * @throws NotFoundException - * @throws TableNotFoundException - */ - public function deleteOne($primary) - { - $row = $this->getTable()::findRow($primary); - - if (!$row) { - throw new NotFoundException('Record not found'); - } - return $row->delete(); - } -} diff --git a/src/Db/Db.php b/src/Db/Db.php index daec17bf..1c1974b7 100644 --- a/src/Db/Db.php +++ b/src/Db/Db.php @@ -37,7 +37,7 @@ class Db * @var array * @link http://php.net/manual/en/pdo.construct.php */ - protected $connect = [ + protected array $connect = [ 'type' => 'mysql', 'host' => 'localhost', 'name' => '', @@ -52,14 +52,14 @@ class Db * @var array * @link http://php.net/manual/en/pdo.setattribute.php */ - protected $attributes = [ + protected array $attributes = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ]; /** - * @var PDO PDO instance + * @var PDO|null PDO instance */ - protected $handler; + protected ?PDO $handler = null; /** * Setup connection @@ -75,10 +75,10 @@ class Db * ]); * * - * @param array $connect options + * @param array $connect options * - * @throws ConfigurationException * @return void + * @throws ConfigurationException */ public function setConnect(array $connect): void { @@ -110,7 +110,7 @@ private function checkConnect(): void /** * Setup attributes for PDO connect * - * @param array $attributes + * @param array $attributes * * @return void */ @@ -130,12 +130,28 @@ public function connect(): bool try { $this->checkConnect(); $this->log('Connect to ' . $this->connect['host']); - $this->handler = new PDO( - $this->connect['type'] . ':host=' . $this->connect['host'] . ';dbname=' . $this->connect['name'], - $this->connect['user'], - $this->connect['pass'], - $this->connect['options'] - ); + + switch ($this->connect['type']) { + case 'sqlite': + $pdoOptions = [ + $this->connect['type'] . ':' . $this->connect['name'] + ]; + break; + case 'mysql': + case 'pgsql': + default: + $pdoOptions = [ + $this->connect['type'] . + ':host=' . $this->connect['host'] . + ';dbname=' . $this->connect['name'], + $this->connect['user'], + $this->connect['pass'], + $this->connect['options'] + ]; + break; + } + + $this->handler = new PDO(...$pdoOptions); foreach ($this->attributes as $attribute => $value) { $this->handler->setAttribute($attribute, $value); @@ -177,14 +193,14 @@ public function handler(): PDO /** * Prepare SQL query and return PDO Statement * - * @param string $sql SQL query with placeholders - * @param array $params params for query placeholders + * @param string $sql SQL query with placeholders + * @param array $params params for query placeholders * + * @return PDOStatement + * @throws DbException * @todo Switch to PDO::activeQueryString() when it will be possible * @link https://wiki.php.net/rfc/debugging_pdo_prepared_statement_emulation * - * @return PDOStatement - * @throws DbException */ protected function prepare(string $sql, array $params = []): PDOStatement { @@ -204,8 +220,8 @@ protected function prepare(string $sql, array $params = []): PDOStatement * $db->quote($_GET['id']) * * - * @param string $value - * @param int $type + * @param string $value + * @param int $type * * @return string * @throws DbException @@ -218,7 +234,7 @@ public function quote(string $value, int $type = PDO::PARAM_STR): string /** * Quote a string so it can be safely used as a table or column name * - * @param string $identifier + * @param string $identifier * * @return string */ @@ -243,11 +259,11 @@ public function quoteIdentifier(string $identifier): string * $db->query("SET NAMES 'utf8'"); * * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * "UPDATE users SET name = :name WHERE id = :id" - * @param array $params params for query placeholders (optional) + * @param array $params params for query placeholders (optional) * array (':name' => 'John', ':id' => '123') - * @param array $types Types of params (optional) + * @param array $types Types of params (optional) * array (':name' => \PDO::PARAM_STR, ':id' => \PDO::PARAM_INT) * * @return integer the number of rows @@ -286,7 +302,7 @@ public function select(...$select): Query\Select /** * Create new query insert builder * - * @param string $table + * @param string $table * * @return Query\Insert */ @@ -300,7 +316,7 @@ public function insert(string $table): Query\Insert /** * Create new query update builder * - * @param string $table + * @param string $table * * @return Query\Update */ @@ -314,7 +330,7 @@ public function update(string $table): Query\Update /** * Create new query update builder * - * @param string $table + * @param string $table * * @return Query\Delete */ @@ -333,9 +349,9 @@ public function delete(string $table): Query\Delete * $db->fetchOne("SELECT COUNT(*) FROM users"); * * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * "SELECT * FROM users WHERE name = :name AND pass = :pass" - * @param array $params params for query placeholders (optional) + * @param array $params params for query placeholders (optional) * array (':name' => 'John', ':pass' => '123456') * * @return string|false @@ -360,9 +376,9 @@ public function fetchOne(string $sql, array $params = []) * $db->fetchRow("SELECT name, email FROM users WHERE id = :id", [':id'=>$id]); * * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * "SELECT * FROM users WHERE name = :name AND pass = :pass" - * @param array $params params for query placeholders (optional) + * @param array $params params for query placeholders (optional) * array (':name' => 'John', ':pass' => '123456') * * @return array|false array ('name' => 'John', 'email' => 'john@smith.com') @@ -385,9 +401,9 @@ public function fetchRow(string $sql, array $params = []) * $db->fetchAll("SELECT * FROM users WHERE ip = ?", ['192.168.1.1']); * * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * "SELECT * FROM users WHERE ip = :ip" - * @param array $params params for query placeholders (optional) + * @param array $params params for query placeholders (optional) * array (':ip' => '127.0.0.1') * * @return array[]|false @@ -405,9 +421,9 @@ public function fetchAll(string $sql, array $params = []) /** * Returns an array containing one column from the result set rows * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * "SELECT id FROM users WHERE ip = :ip" - * @param array $params params for query placeholders (optional) + * @param array $params params for query placeholders (optional) * array (':ip' => '127.0.0.1') * * @return array|false @@ -431,10 +447,10 @@ public function fetchColumn(string $sql, array $params = []) * $db->fetchGroup("SELECT ip, COUNT(id) FROM users GROUP BY ip", []); * * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * "SELECT ip, id FROM users" - * @param array $params params for query placeholders (optional) - * @param mixed $object + * @param array $params params for query placeholders (optional) + * @param mixed $object * * @return array|false * @throws DbException @@ -458,9 +474,9 @@ public function fetchGroup(string $sql, array $params = [], $object = null) * * Group by first column * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * "SELECT ip, id FROM users" - * @param array $params params for query placeholders (optional) + * @param array $params params for query placeholders (optional) * * @return array|false * @throws DbException @@ -479,9 +495,9 @@ public function fetchColumnGroup(string $sql, array $params = []) * * Group by first unique column * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * "SELECT email, name, sex FROM users" - * @param array $params params for query placeholders (optional) + * @param array $params params for query placeholders (optional) * * @return array|false * @throws DbException @@ -498,9 +514,9 @@ public function fetchUniqueGroup(string $sql, array $params = []) /** * Returns a key-value array * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * "SELECT id, username FROM users WHERE ip = :ip" - * @param array $params params for query placeholders (optional) + * @param array $params params for query placeholders (optional) * array (':ip' => '127.0.0.1') * * @return array|false @@ -528,11 +544,11 @@ public function fetchPairs(string $sql, array $params = []) * $someClass = $db->fetchObject('SELECT * FROM some_table WHERE id = ?', [$id], $someClass); * * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * "SELECT * FROM users WHERE name = :name AND pass = :pass" - * @param array $params params for query placeholders (optional) + * @param array $params params for query placeholders (optional) * array (':name' => 'John', ':pass' => '123456') - * @param mixed $object + * @param mixed $object * * @return mixed * @throws DbException @@ -558,11 +574,11 @@ public function fetchObject(string $sql, array $params = [], $object = 'stdClass /** * Returns an array of objects containing the result set * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * "SELECT * FROM users WHERE name = :name AND pass = :pass" - * @param array $params params for query placeholders (optional) + * @param array $params params for query placeholders (optional) * array (':name' => 'John', ':pass' => '123456') - * @param mixed $object Class name or instance + * @param mixed $object Class name or instance * * @return array|false * @throws DbException @@ -587,12 +603,12 @@ public function fetchObjects(string $sql, array $params = [], $object = null) /** * Returns an array of linked objects containing the result set * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * "SELECT '__users', u.*, '__users_profile', up.* * FROM users u * LEFT JOIN users_profile up ON up.userId = u.id * WHERE u.name = :name" - * @param array $params params for query placeholders (optional) + * @param array $params params for query placeholders (optional) * array (':name' => 'John') * * @return array|false @@ -624,7 +640,7 @@ public function fetchRelations(string $sql, array $params = []) * }) * * - * @param callable $process callable structure - closure function or class with __invoke() method + * @param callable $process callable structure - closure function or class with __invoke() method * * @return mixed|bool * @throws DbException @@ -656,8 +672,8 @@ protected function ok(): void /** * Log queries by Application * - * @param string $query SQL query for logs - * @param array $context + * @param string $query SQL query for logs + * @param array $context * * @return void */ diff --git a/src/Db/Exception/TableNotFoundException.php b/src/Db/Exception/TableNotFoundException.php index 0a6ec6e6..5e8691df 100644 --- a/src/Db/Exception/TableNotFoundException.php +++ b/src/Db/Exception/TableNotFoundException.php @@ -12,7 +12,7 @@ namespace Bluz\Db\Exception; /** - * Table Not Found Exception + * Table Didn't Find * * @package Bluz\Db\Exception * @author Eugene Zabolotniy diff --git a/src/Db/Query/AbstractBuilder.php b/src/Db/Query/AbstractBuilder.php index d85876db..754a62e4 100644 --- a/src/Db/Query/AbstractBuilder.php +++ b/src/Db/Query/AbstractBuilder.php @@ -27,29 +27,29 @@ abstract class AbstractBuilder /** * @var array list of table aliases */ - protected $aliases = []; + protected array $aliases = []; /** * @var array the query parameters */ - protected $params = []; + protected array $params = []; /** * @var array the parameter type map of this query */ - protected $types = []; + protected array $types = []; /** * @var string the complete SQL string for this query */ - protected $sql; + protected string $sql; /** * Execute this query using the bound parameters and their types * - * @return integer|string|array + * @return array|int|string */ - public function execute() + public function execute(): array|int|string { return Db::query($this->getSql(), $this->params, $this->types); } @@ -98,7 +98,7 @@ public function getQuery(): string /** * Gets a (previously set) query parameter of the query being constructed * - * @param mixed $key The key (index or name) of the bound parameter + * @param mixed $key The key (index or name) of the bound parameter * * @return mixed The value of the bound parameter. */ @@ -120,9 +120,9 @@ public function getParam($key) * ->setParameter(':user_id', 1); * * - * @param string|int|null $key The parameter position or name - * @param mixed $value The parameter value - * @param integer $type PDO::PARAM_* + * @param string|int|null $key The parameter position or name + * @param mixed $value The parameter value + * @param int $type PDO::PARAM_* * * @return self */ @@ -164,8 +164,8 @@ public function getParams(): array * ]); * * - * @param array $params The query parameters to set - * @param array $types The query parameters types to set + * @param array $params The query parameters to set + * @param array $types The query parameters types to set * * @return self */ @@ -184,7 +184,7 @@ public function setParams(array $params, array $types = []): self * $builder->prepareCondition("WHERE id IN (?)", [..,..]); * * - * @param array $args + * @param array $args * * @return string */ @@ -204,9 +204,7 @@ protected function prepareCondition(array $args = []): string } unset($value); - $condition = preg_replace('/(\:REPLACE\:)/', '?', $condition); - - return $condition; + return preg_replace('/(:REPLACE:)/', '?', $condition); } /** diff --git a/src/Db/Query/CompositeBuilder.php b/src/Db/Query/CompositeBuilder.php index fb861782..b8af91e6 100644 --- a/src/Db/Query/CompositeBuilder.php +++ b/src/Db/Query/CompositeBuilder.php @@ -21,18 +21,18 @@ class CompositeBuilder implements \Countable /** * @var string type AND|OR */ - private $type; + private string $type; /** * @var array parts of the composite expression */ - private $parts = []; + private array $parts = []; /** * Constructor * - * @param array $parts parts of the composite expression - * @param string $type AND|OR + * @param array $parts parts of the composite expression + * @param string $type AND|OR */ public function __construct(array $parts = [], string $type = 'AND') { @@ -44,27 +44,27 @@ public function __construct(array $parts = [], string $type = 'AND') /** * Adds a set of expressions to composite expression * - * @param array $parts + * @param array $parts * * @return CompositeBuilder */ - public function addParts($parts): CompositeBuilder + public function addParts(array $parts): CompositeBuilder { + $parts = array_filter($parts); foreach ($parts as $part) { $this->addPart($part); } - return $this; } /** * Adds an expression to composite expression * - * @param mixed $part + * @param string|CompositeBuilder $part * * @return CompositeBuilder */ - public function addPart($part): CompositeBuilder + public function addPart(string|CompositeBuilder $part): CompositeBuilder { if (!empty($part) || ($part instanceof self && $part->count() > 0)) { $this->parts[] = $part; @@ -100,7 +100,7 @@ public function count(): int public function __toString() { if ($this->count() === 1) { - return (string) $this->parts[0]; + return (string)$this->parts[0]; } return '(' . implode(') ' . $this->type . ' (', $this->parts) . ')'; } diff --git a/src/Db/Query/Delete.php b/src/Db/Query/Delete.php index 2bd1cd8e..cab8f7fc 100644 --- a/src/Db/Query/Delete.php +++ b/src/Db/Query/Delete.php @@ -27,10 +27,10 @@ class Delete extends AbstractBuilder /** * @var string Table name */ - protected $table; + protected string $table; /** - * {@inheritdoc} + * @inheritDoc * * @return string */ @@ -54,11 +54,11 @@ public function getSql(): string * ->where('id = ?'); * * - * @param string $table The table whose rows are subject to the update + * @param string $table The table whose rows are subject to the update * * @return Delete instance */ - public function delete($table): Delete + public function delete(string $table): Delete { $this->table = $table; return $this; diff --git a/src/Db/Query/Insert.php b/src/Db/Query/Insert.php index 9714fa4e..1eeaa110 100644 --- a/src/Db/Query/Insert.php +++ b/src/Db/Query/Insert.php @@ -25,16 +25,16 @@ class Insert extends AbstractBuilder /** * @var string Table name */ - protected $table; + protected string $table; /** - * {@inheritdoc} + * @inheritDoc * - * @param null $sequence + * @param null $sequence * - * @return integer|string|array + * @return : array|int|string */ - public function execute($sequence = null) + public function execute($sequence = null): array|int|string { $result = Db::query($this->getSql(), $this->params, $this->types); if ($result) { @@ -44,7 +44,7 @@ public function execute($sequence = null) } /** - * {@inheritdoc} + * @inheritDoc * * @return string */ @@ -68,11 +68,11 @@ public function getSql(): string * ->set('password', md5('password')); * * - * @param string $table The table into which the rows should be inserted + * @param string $table The table into which the rows should be inserted * * @return Insert instance */ - public function insert($table): Insert + public function insert(string $table): Insert { $this->table = $table; return $this; diff --git a/src/Db/Query/Select.php b/src/Db/Query/Select.php index 8ea85c75..58e08f72 100644 --- a/src/Db/Query/Select.php +++ b/src/Db/Query/Select.php @@ -36,23 +36,23 @@ class Select extends AbstractBuilder * * @var array[] */ - protected $select = []; - protected $groupBy = []; - protected $having = null; + protected array $select = []; + protected array $groupBy = []; + protected string|CompositeBuilder|null $having = null; /** * @var mixed PDO fetch types or object class */ - protected $fetchType = \PDO::FETCH_ASSOC; + protected mixed $fetchType = \PDO::FETCH_ASSOC; /** - * {@inheritdoc} + * @inheritDoc * - * @param integer|string|object $fetchType + * @param object|int|string|null $fetchType * - * @return integer|string|array + * @return array|int|string */ - public function execute($fetchType = null) + public function execute(object|int|string $fetchType = null): array|int|string { if (!$fetchType) { $fetchType = $this->fetchType; @@ -72,18 +72,18 @@ public function execute($fetchType = null) /** * Setup fetch type, any of PDO, or any Class * - * @param string $fetchType + * @param object|int|string $fetchType * * @return Select instance */ - public function setFetchType($fetchType): Select + public function setFetchType(object|int|string $fetchType): Select { $this->fetchType = $fetchType; return $this; } /** - * {@inheritdoc} + * @inheritDoc */ public function getSql(): string { @@ -109,7 +109,7 @@ public function getSql(): string * ->leftJoin('u', 'phone', 'p', 'u.id = p.user_id'); * * - * @param string[] $select the selection expressions + * @param string[] $select the selection expressions * * @return Select instance */ @@ -132,7 +132,7 @@ public function select(...$select): Select * ->leftJoin('u', 'phone', 'u.id = p.user_id'); * * - * @param string[] $select the selection expression + * @param string[] $select the selection expression * * @return Select instance */ @@ -145,7 +145,7 @@ public function addSelect(string ...$select): Select /** * Get current select query part * - * @return array + * @return string[] */ public function getSelect(): array { @@ -165,7 +165,7 @@ public function getSelect(): array * ->groupBy('u.id'); * * - * @param string[] $groupBy the grouping expression + * @param string[] $groupBy the grouping expression * * @return Select instance */ @@ -188,7 +188,7 @@ public function groupBy(string ...$groupBy): Select * ->addGroupBy('u.createdAt') * * - * @param string[] $groupBy the grouping expression + * @param string[] $groupBy the grouping expression * * @return Select instance */ @@ -202,7 +202,7 @@ public function addGroupBy(string ...$groupBy): Select * Specifies a restriction over the groups of the query. * Replaces any previous having restrictions, if any * - * @param string[] $conditions the query restriction predicates + * @param string[] $conditions the query restriction predicates * * @return Select */ @@ -216,7 +216,7 @@ public function having(...$conditions): Select * Adds a restriction over the groups of the query, forming a logical * conjunction with any existing having restrictions * - * @param string[] $conditions the query restriction predicates + * @param string[] $conditions the query restriction predicates * * @return Select */ @@ -239,7 +239,7 @@ public function andHaving(...$conditions): Select * Adds a restriction over the groups of the query, forming a logical * disjunction with any existing having restrictions * - * @param string[] $conditions the query restriction predicates + * @param string[] $conditions the query restriction predicates * * @return Select */ @@ -261,7 +261,7 @@ public function orHaving(...$conditions): Select /** * Setup offset like a page number, start from 1 * - * @param integer $page + * @param int $page * * @return Select * @throws DbException diff --git a/src/Db/Query/Traits/From.php b/src/Db/Query/Traits/From.php index c76867e7..ac3999a0 100644 --- a/src/Db/Query/Traits/From.php +++ b/src/Db/Query/Traits/From.php @@ -11,6 +11,7 @@ namespace Bluz\Db\Query\Traits; +use Bluz\Db\Query\Select; use Bluz\Proxy\Db; /** @@ -35,7 +36,7 @@ trait From * * @var array */ - protected $from = []; + protected array $from = []; /** * @@ -50,7 +51,7 @@ trait From * * @var array[] */ - protected $join = []; + protected array $join = []; /** * Set FROM @@ -65,12 +66,11 @@ trait From * ->from('users', 'u') * * - * @param string $from The table - * @param string $alias The alias of the table - * - * @return $this + * @param string $from The table + * @param string $alias The alias of the table + * @return Select */ - public function from(string $from, string $alias): self + public function from(string $from, string $alias): Select { $this->aliases[] = $alias; @@ -94,14 +94,13 @@ public function from(string $from, string $alias): self * ->join('u', 'phone', 'p', 'p.is_primary = 1'); * * - * @param string $fromAlias The alias that points to a from clause - * @param string $join The table name to join - * @param string $alias The alias of the join table - * @param string $condition The condition for the join - * - * @return $this + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string|null $condition The condition for the join + * @return Select */ - public function join(string $fromAlias, string $join, string $alias, string $condition = null): self + public function join(string $fromAlias, string $join, string $alias, string $condition = null): Select { return $this->innerJoin($fromAlias, $join, $alias, $condition); } @@ -118,14 +117,13 @@ public function join(string $fromAlias, string $join, string $alias, string $con * ->innerJoin('u', 'phone', 'p', 'p.is_primary = 1'); * * - * @param string $fromAlias The alias that points to a from clause - * @param string $join The table name to join - * @param string $alias The alias of the join table - * @param string $condition The condition for the join - * - * @return $this + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string|null $condition The condition for the join + * @return Select */ - public function innerJoin(string $fromAlias, string $join, string $alias, string $condition = null): self + public function innerJoin(string $fromAlias, string $join, string $alias, string $condition = null): Select { return $this->addJoin('inner', $fromAlias, $join, $alias, $condition); } @@ -142,14 +140,13 @@ public function innerJoin(string $fromAlias, string $join, string $alias, string * ->leftJoin('u', 'phone', 'p', 'p.is_primary = 1'); * * - * @param string $fromAlias The alias that points to a from clause - * @param string $join The table name to join - * @param string $alias The alias of the join table - * @param string $condition The condition for the join - * - * @return $this + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string|null $condition The condition for the join + * @return Select */ - public function leftJoin(string $fromAlias, string $join, string $alias, string $condition = null): self + public function leftJoin(string $fromAlias, string $join, string $alias, string $condition = null): Select { return $this->addJoin('left', $fromAlias, $join, $alias, $condition); } @@ -166,14 +163,13 @@ public function leftJoin(string $fromAlias, string $join, string $alias, string * ->rightJoin('u', 'phone', 'p', 'p.is_primary = 1'); * * - * @param string $fromAlias The alias that points to a from clause - * @param string $join The table name to join - * @param string $alias The alias of the join table - * @param string $condition The condition for the join - * - * @return $this + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string|null $condition The condition for the join + * @return Select */ - public function rightJoin(string $fromAlias, string $join, string $alias, string $condition = null): self + public function rightJoin(string $fromAlias, string $join, string $alias, string $condition = null): Select { return $this->addJoin('right', $fromAlias, $join, $alias, $condition); } @@ -181,13 +177,12 @@ public function rightJoin(string $fromAlias, string $join, string $alias, string /** * addJoin() * - * @param string $type The type of join - * @param string $fromAlias The alias that points to a from clause - * @param string $join The table name to join - * @param string $alias The alias of the join table - * @param string $condition The condition for the join - * - * @return $this + * @param string $type The type of join + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string|null $condition The condition for the join + * @return Select */ protected function addJoin( string $type, @@ -195,7 +190,7 @@ protected function addJoin( string $join, string $alias, string $condition = null - ): self { + ): Select { $this->aliases[] = $alias; $this->join[$fromAlias][] = [ @@ -210,11 +205,10 @@ protected function addJoin( /** * setFromQueryPart * - * @param string $table - * - * @return self + * @param string $table + * @return Select */ - protected function setFromQueryPart($table): self + protected function setFromQueryPart(string $table): Select { return $this->from($table, $table); } @@ -241,11 +235,11 @@ protected function prepareFrom(): string /** * Generate SQL string for JOINs * - * @param string $fromAlias The alias of the table + * @param string $fromAlias The alias of the table * * @return string */ - protected function prepareJoins($fromAlias): string + protected function prepareJoins(string $fromAlias): string { if (!isset($this->join[$fromAlias])) { return ''; diff --git a/src/Db/Query/Traits/Limit.php b/src/Db/Query/Traits/Limit.php index 6e6e8c4d..e398c0e3 100644 --- a/src/Db/Query/Traits/Limit.php +++ b/src/Db/Query/Traits/Limit.php @@ -11,6 +11,10 @@ namespace Bluz\Db\Query\Traits; +use Bluz\Db\Query\Delete; +use Bluz\Db\Query\Select; +use Bluz\Db\Query\Update; + /** * Limit Trait * @@ -27,22 +31,21 @@ trait Limit /** * @var integer the maximum number of results to retrieve/update/delete */ - protected $limit; + protected int $limit = 0; /** * @var integer the index of the first result to retrieve. */ - protected $offset = 0; + protected int $offset = 0; /** * Sets the maximum number of results to retrieve/update/delete * - * @param integer $limit The maximum number of results to retrieve - * @param integer $offset The offset of the query - * - * @return $this + * @param int $limit The maximum number of results to retrieve + * @param int $offset The offset of the query + * @return Delete|Select|Update */ - public function limit(int $limit, int $offset = 0): self + public function limit(int $limit, int $offset = 0): Delete|Update|Select { $this->setLimit($limit); $this->setOffset($offset); @@ -52,11 +55,10 @@ public function limit(int $limit, int $offset = 0): self /** * Setup limit for the query * - * @param integer $limit - * - * @return $this + * @param int $limit + * @return Delete|Select|Update */ - public function setLimit(int $limit): self + public function setLimit(int $limit): Delete|Update|Select { $this->limit = $limit; return $this; @@ -65,11 +67,11 @@ public function setLimit(int $limit): self /** * Setup offset for the query * - * @param integer $offset + * @param int $offset * - * @return $this + * @return Delete|Select|Update */ - public function setOffset(int $offset): self + public function setOffset(int $offset): Delete|Update|Select { $this->offset = $offset; return $this; diff --git a/src/Db/Query/Traits/Order.php b/src/Db/Query/Traits/Order.php index 71e5545a..81914c1e 100644 --- a/src/Db/Query/Traits/Order.php +++ b/src/Db/Query/Traits/Order.php @@ -11,6 +11,9 @@ namespace Bluz\Db\Query\Traits; +use Bluz\Db\Query\Delete; +use Bluz\Db\Query\Select; + /** * Order Trait * @@ -27,18 +30,18 @@ trait Order /** * @var array */ - protected $orderBy = []; + protected array $orderBy = []; /** * Specifies an ordering for the query results * Replaces any previously specified orderings, if any * - * @param string $sort Sort expression - * @param string $order Sort direction (ASC or DESC) + * @param string $sort Sort expression + * @param string $order Sort direction (ASC or DESC) * - * @return $this + * @return Delete|Select */ - public function orderBy(string $sort, string $order = 'ASC'): self + public function orderBy(string $sort, string $order = 'ASC'): Delete|Select { $order = 'ASC' === strtoupper($order) ? 'ASC' : 'DESC'; $this->orderBy = [$sort => $order]; @@ -48,12 +51,12 @@ public function orderBy(string $sort, string $order = 'ASC'): self /** * Adds an ordering to the query results * - * @param string $sort Sort expression - * @param string $order Sort direction (ASC or DESC) + * @param string $sort Sort expression + * @param string $order Sort direction (ASC or DESC) * - * @return $this + * @return Delete|Select */ - public function addOrderBy(string $sort, string $order = 'ASC'): self + public function addOrderBy(string $sort, string $order = 'ASC'): Delete|Select { $order = 'ASC' === strtoupper($order) ? 'ASC' : 'DESC'; $this->orderBy[$sort] = $order; diff --git a/src/Db/Query/Traits/Set.php b/src/Db/Query/Traits/Set.php index 0c2d920f..d374cd75 100644 --- a/src/Db/Query/Traits/Set.php +++ b/src/Db/Query/Traits/Set.php @@ -11,6 +11,8 @@ namespace Bluz\Db\Query\Traits; +use Bluz\Db\Query\Insert; +use Bluz\Db\Query\Update; use Bluz\Proxy\Db; use PDO; @@ -36,7 +38,7 @@ trait Set * * @var array */ - protected $set = []; + protected array $set = []; /** * Set key-value pair @@ -50,13 +52,13 @@ trait Set * ->where('id = ?'); * * - * @param string $key The column to set - * @param string|integer $value The value, expression, placeholder, etc - * @param int $type The type of value on of PDO::PARAM_* params + * @param string $key The column to set + * @param string|integer $value The value, expression, placeholder, etc + * @param int $type The type of value on of PDO::PARAM_* params * - * @return $this + * @return Update|Insert */ - public function set(string $key, $value, $type = PDO::PARAM_STR): self + public function set(string $key, int|string $value, int $type = PDO::PARAM_STR): Insert|Update { $this->setParam(null, $value, $type); $this->set[] = Db::quoteIdentifier($key) . ' = ?'; @@ -77,11 +79,11 @@ public function set(string $key, $value, $type = PDO::PARAM_STR): self * ->where('u.id = ?'); * * - * @param array $data + * @param array $data * - * @return $this + * @return Insert|Update */ - public function setArray(array $data): self + public function setArray(array $data): Insert|Update { foreach ($data as $key => $value) { $this->set($key, $value); diff --git a/src/Db/Query/Traits/Where.php b/src/Db/Query/Traits/Where.php index c7bb8dad..b2a760c0 100644 --- a/src/Db/Query/Traits/Where.php +++ b/src/Db/Query/Traits/Where.php @@ -12,6 +12,9 @@ namespace Bluz\Db\Query\Traits; use Bluz\Db\Query\CompositeBuilder; +use Bluz\Db\Query\Delete; +use Bluz\Db\Query\Select; +use Bluz\Db\Query\Update; /** * Order Trait @@ -29,7 +32,7 @@ trait Where /** * @var string|CompositeBuilder|null */ - protected $where; + protected mixed $where = null; /** * Set WHERE condition @@ -45,11 +48,10 @@ trait Where * ; * * - * @param string[] $conditions optional the query restriction predicates - * - * @return $this + * @param array $conditions optional the query restriction predicates + * @return Delete|Select|Update */ - public function where(...$conditions): self + public function where(...$conditions): Delete|Select|Update { $this->where = $this->prepareCondition($conditions); return $this; @@ -69,11 +71,10 @@ public function where(...$conditions): self * ->andWhere('u.is_active = ?', 1); * * - * @param string[] $conditions Optional the query restriction predicates - * - * @return $this + * @param array $conditions Optional the query restriction predicates + * @return Delete|Select|Update */ - public function andWhere(...$conditions): self + public function andWhere(...$conditions): Delete|Select|Update { $condition = $this->prepareCondition($conditions); @@ -102,11 +103,10 @@ public function andWhere(...$conditions): self * ->orWhere('u.id = ?', 2); * * - * @param string[] $conditions Optional the query restriction predicates - * - * @return $this + * @param array $conditions Optional the query restriction predicates + * @return Delete|Select|Update */ - public function orWhere(...$conditions): self + public function orWhere(...$conditions): Delete|Select|Update { $condition = $this->prepareCondition($conditions); diff --git a/src/Db/Query/Update.php b/src/Db/Query/Update.php index 549094d1..5efbf5ef 100644 --- a/src/Db/Query/Update.php +++ b/src/Db/Query/Update.php @@ -27,10 +27,10 @@ class Update extends AbstractBuilder /** * @var string Table name */ - protected $table; + protected string $table; /** - * {@inheritdoc} + * @inheritDoc */ public function getSql(): string { @@ -54,11 +54,11 @@ public function getSql(): string * ->where('id = ?'); * * - * @param string $table the table whose rows are subject to the update + * @param string $table the table whose rows are subject to the update * * @return Update instance */ - public function update($table): Update + public function update(string $table): Update { $this->table = $table; return $this; diff --git a/src/Db/Relations.php b/src/Db/Relations.php index fc10c1a5..6c8f64ad 100644 --- a/src/Db/Relations.php +++ b/src/Db/Relations.php @@ -35,7 +35,7 @@ class Relations * * @var array */ - protected static $relations; + protected static array $relations; /** * Class map, i.e. @@ -48,7 +48,7 @@ class Relations * * @var array */ - protected static $modelClassMap; + protected static array $modelClassMap; /** * Setup relation between two models @@ -141,7 +141,7 @@ public static function findRelation(Row $row, string $relation): array * * @param string $modelOne Table * @param string $modelTwo Target table - * @param array $keys Keys from first table + * @param array $keys Keys from first table * * @return array * @throws Exception\RelationNotFoundException @@ -168,9 +168,9 @@ public static function findRelations(string $modelOne, string $modelTwo, array $ /* @var Query\Select $tableTwoSelect */ $tableTwoSelect = $tableTwoClass::getInstance()::select(); - // check many to many relation + // check many-to-many relation if (is_int(\array_keys($relations)[0])) { - // many to many relation over third table + // many-to-many relation over third table $modelThree = $relations[0]; // relations between target table and third table @@ -277,7 +277,7 @@ public static function fetch(array $input): array foreach ($input as $i => $row) { $model = ''; foreach ($row as $key => $value) { - if (strpos($key, '__') === 0) { + if (str_starts_with($key, '__')) { $model = substr($key, 2); continue; } diff --git a/src/Db/Row.php b/src/Db/Row.php index aff3d9f7..d5ba6dd6 100644 --- a/src/Db/Row.php +++ b/src/Db/Row.php @@ -11,57 +11,15 @@ namespace Bluz\Db; -use Bluz\Common\Container; -use Bluz\Db\Exception\DbException; -use Bluz\Db\Exception\InvalidPrimaryKeyException; -use Bluz\Db\Exception\TableNotFoundException; - /** * Db Table Row * - * Example of Users\Row - * - * namespace Application\Users; - * class Row extends \Bluz\Db\Row - * { - * public function beforeInsert(): void - * { - * $this->created = gmdate('Y-m-d H:i:s'); - * } - * - * public function beforeUpdate(): void - * { - * $this->updated = gmdate('Y-m-d H:i:s'); - * } - * } - * - * $userRow = new \Application\Users\Row(); - * $userRow -> login = 'username'; - * $userRow -> save(); - * - * * @package Bluz\Db * @author Anton Shevchuk * @link https://github.com/bluzphp/framework/wiki/Db-Row */ -abstract class Row implements RowInterface, \JsonSerializable, \ArrayAccess +abstract class Row implements RowInterface { - use Container\Container; - use Container\ArrayAccess; - use Container\JsonSerialize; - use Container\MagicAccess; - use Traits\TableProperty; - use Traits\RowRelations; - - /** - * This is set to a copy of $data when the data is fetched from - * a database, specified as a new tuple in the constructor, or - * when dirty data is posted to the database with save(). - * - * @var array - */ - protected $clean = []; - /** * Create Row instance * @@ -69,371 +27,77 @@ abstract class Row implements RowInterface, \JsonSerializable, \ArrayAccess */ public function __construct(array $data = []) { - // original cleaner data - $this->clean = $this->toArray(); - // not clean data, but not modified if (count($data)) { $this->setFromArray($data); } - $this->afterRead(); - } - - /** - * List of required for serialization properties - * - * @return string[] - */ - public function __sleep() - { - return ['container', 'clean']; - } - - /** - * Cast to string as class name - * - * @return string - */ - public function __toString() - { - return static::class; - } - - /** - * Magic method for var_dump() - * - * @return array - * @see var_dump() - */ - public function __debugInfo() - { - return [ - 'DATA::CLEAN' => $this->clean, - 'DATA::RAW' => $this->container, - 'RELATIONS' => $this->relations ?? [] - ]; - } - - /** - * Validate input data - * - * @param array $data - * - * @return bool - */ - public function validate($data): bool - { - return true; - } - - /** - * Assert input data - * - * @param array $data - * - * @return void - */ - public function assert($data): void - { - return; - } - - /** - * Saves the properties to the database. - * - * This performs an intelligent insert/update, and reloads the - * properties with fresh data from the table on success. - * - * @return mixed The primary key value(s), as an associative array if the - * key is compound, or a scalar if the key is single-column - * @throws DbException - * @throws InvalidPrimaryKeyException - * @throws TableNotFoundException - */ - public function save() - { - $this->beforeSave(); - /** - * If the primary key is empty, this is an INSERT of a new row. - * Otherwise check primary key updated or not, if it changed - INSERT - * otherwise UPDATE - */ - if (!count(array_filter($this->getPrimaryKey()))) { - $result = $this->doInsert(); - } elseif (count(array_diff_assoc($this->getPrimaryKey(), $this->clean))) { - $result = $this->doInsert(); - } else { - $result = $this->doUpdate(); - } - $this->afterSave(); - return $result; } /** - * Insert row to Db - * - * @return mixed The primary key value(s), as an associative array if the - * key is compound, or a scalar if the key is single-column - * @throws InvalidPrimaryKeyException - * @throws TableNotFoundException + * @param $key + * @return mixed */ - protected function doInsert() + public function __get($key) { - /** - * Run pre-INSERT logic - */ - $this->beforeInsert(); - - $data = $this->toArray(); - /** - * Execute validator logic - * Can throw ValidatorException - */ - $this->assert($data); - - $table = $this->getTable(); - - /** - * Execute the INSERT (this may throw an exception) - */ - $primaryKey = $table::insert($data); - - /** - * Normalize the result to an array indexed by primary key column(s) - */ - $tempPrimaryKey = $table->getPrimaryKey(); - $newPrimaryKey = [current($tempPrimaryKey) => $primaryKey]; - - /** - * Save the new primary key value in object. The primary key may have - * been generated by a sequence or auto-increment mechanism, and this - * merge should be done before the afterInsert() method is run, so the - * new values are available for logging, etc. - */ - $this->setFromArray($newPrimaryKey); - - /** - * Run post-INSERT logic - */ - $this->afterInsert(); - - /** - * Update the "clean" to reflect that the data has been inserted. - */ - $this->clean = $this->toArray(); - - return $newPrimaryKey; - } - - /** - * Update row - * - * @return integer The number of rows updated - * @throws InvalidPrimaryKeyException - * @throws TableNotFoundException - * @throws DbException - */ - protected function doUpdate(): int - { - /** - * Run pre-UPDATE logic - */ - $this->beforeUpdate(); - - $data = $this->toArray(); - - /** - * Execute validator logic - * Can throw ValidatorException - */ - $this->assert($data); - - $primaryKey = $this->getPrimaryKey(); - - /** - * Compare the data to the modified fields array to discover - * which columns have been changed. - */ - $diffData = array_diff_assoc($data, $this->clean); - - /* @var Table $table */ - $table = $this->getTable(); - - $diffData = $table::filterColumns($diffData); - - /** - * Execute the UPDATE (this may throw an exception) - * Do this only if data values were changed. - * Use the $diffData variable, so the UPDATE statement - * includes SET terms only for data values that changed. - */ - $result = 0; - if (count($diffData) > 0) { - $result = $table::update($diffData, $primaryKey); + if (property_exists($this, $key)) { + return $this->{$key}; } - - /** - * Run post-UPDATE logic. Do this before the _refresh() - * so the _afterUpdate() function can tell the difference - * between changed data and clean (pre-changed) data. - */ - $this->afterUpdate(); - - /** - * Refresh the data just in case triggers in the RDBMS changed - * any columns. Also this resets the "clean". - */ - $this->clean = $this->toArray(); - - return $result; - } - - /** - * Delete existing row - * - * @return bool Removed or not - * @throws InvalidPrimaryKeyException - * @throws TableNotFoundException - */ - public function delete(): bool - { - /** - * Execute pre-DELETE logic - */ - $this->beforeDelete(); - - $primaryKey = $this->getPrimaryKey(); - - /** - * Execute the DELETE (this may throw an exception) - */ - $table = $this->getTable(); - $result = $table::delete($primaryKey); - - /** - * Execute post-DELETE logic - */ - $this->afterDelete(); - - /** - * Reset all fields to null to indicate that the row is not there - */ - $this->resetArray(); - - return $result > 0; - } - - /** - * Retrieves an associative array of primary keys, if it exists - * - * @return array - * @throws InvalidPrimaryKeyException - * @throws TableNotFoundException - */ - protected function getPrimaryKey(): array - { - $primary = array_flip($this->getTable()->getPrimaryKey()); - - return array_intersect_key($this->toArray(), $primary); - } - - /** - * Refreshes properties from the database - * - * @return void - */ - public function refresh(): void - { - $this->setFromArray($this->clean); - $this->afterRead(); + return null; } /** - * After read data from Db - * - * @return void - */ - protected function afterRead(): void - { - } - - /** - * Allows pre-insert and pre-update logic to be applied to row. - * Subclasses may override this method - * - * @return void - */ - protected function beforeSave(): void - { - } - - /** - * Allows post-insert and post-update logic to be applied to row. - * Subclasses may override this method - * - * @return void - */ - protected function afterSave(): void - { - } - - /** - * Allows pre-insert logic to be applied to row. - * Subclasses may override this method - * + * @param $key + * @param $value * @return void */ - protected function beforeInsert(): void + public function __set($key, $value): void { + if (property_exists($this, $key)) { + $this->{$key} = $value; + } } /** - * Allows post-insert logic to be applied to row. - * Subclasses may override this method - * - * @return void + * @param $key + * @return bool */ - protected function afterInsert(): void + public function __isset($key): bool { + return property_exists($this, $key); } /** - * Allows pre-update logic to be applied to row. - * Subclasses may override this method - * + * @param string $key * @return void */ - protected function beforeUpdate(): void + public function __unset(string $key): void { + unset($this->{$key}); } /** - * Allows post-update logic to be applied to row. - * Subclasses may override this method - * - * @return void + * @inheritDoc */ - protected function afterUpdate(): void + public function setFromArray(array $data): void { + foreach ($data as $key => $value) { + $this->__set($key, $value); + } } /** - * Allows pre-delete logic to be applied to row. - * Subclasses may override this method - * - * @return void + * @inheritDoc */ - protected function beforeDelete(): void + public function toArray(): array { + return get_object_vars($this); } /** - * Allows post-delete logic to be applied to row. - * Subclasses may override this method - * - * @return void + * @inheritDoc */ - protected function afterDelete(): void + public function jsonSerialize(): array { + return $this->toArray(); } } diff --git a/src/Db/RowInterface.php b/src/Db/RowInterface.php index a17921b3..5532f0c0 100644 --- a/src/Db/RowInterface.php +++ b/src/Db/RowInterface.php @@ -12,7 +12,7 @@ * @package Bluz\Db * @author Anton Shevchuk */ -interface RowInterface +interface RowInterface extends \JsonSerializable { /** * Create Row instance @@ -21,44 +21,19 @@ interface RowInterface */ public function __construct(array $data = []); - /** - * Returns the column/value data as an array - * - * @return array - */ - public function toArray(): array; - /** * Sets all data in the row from an array * - * @param array $data + * @param array $data * * @return void */ public function setFromArray(array $data): void; /** - * Saves the properties to the database. - * - * This performs an intelligent insert/update, and reloads the - * properties with fresh data from the table on success. - * - * @return mixed The primary key value(s), as an associative array if the - * key is compound, or a scalar if the key is single-column - */ - public function save(); - - /** - * Delete existing row - * - * @return bool Removed or not - */ - public function delete(): bool; - - /** - * Refreshes properties from the database + * Returns the column/value data as an array * - * @return void + * @return array */ - public function refresh(): void; + public function toArray(): array; } diff --git a/src/Db/Table.php b/src/Db/Table.php index d30734a3..ba9fef62 100755 --- a/src/Db/Table.php +++ b/src/Db/Table.php @@ -11,6 +11,7 @@ namespace Bluz\Db; +use Bluz\Common\Exception\InitializationException; use Bluz\Common\Instance; use Bluz\Db\Exception\DbException; use Bluz\Db\Exception\InvalidPrimaryKeyException; @@ -50,57 +51,58 @@ abstract class Table implements TableInterface /** * @var string the table name */ - protected $name; + protected string $name = ''; /** * @var string the model name */ - protected $model; + protected string $model = ''; /** * @var array table meta */ - protected $meta = []; + protected array $meta = []; /** * @var string default SQL query for select */ - protected $select = ''; + protected string $select = ''; /** * @var array the primary key column or columns (only as array). */ - protected $primary; + protected array $primary; /** * @var string the sequence name, required for PostgreSQL */ - protected $sequence; + protected string $sequence; /** * @var string row class name */ - protected $rowClass; + protected string $rowClass = ''; /** * Create and initialize Table instance + * @throws InitializationException */ - private function __construct() + public function __construct() { $tableClass = static::class; $namespace = class_namespace($tableClass); - // autodetect model name - if (!$this->model) { - $this->model = substr($namespace, strrpos($namespace, '\\') + 1); + // check table name + if (!$this->name) { + throw new InitializationException('The table name should be set before initialization'); } - // autodetect table name - camelCase to uppercase - if (!$this->name) { - $table = preg_replace('/(?<=\\w)(?=[A-Z])/', '_$1', $this->model); - $this->name = strtolower($table); + // check primary key(s) + if (!$this->primary) { + throw new InitializationException('The table primary key(s) should be set before initialization'); } + // autodetect row class if (!$this->rowClass) { $this->rowClass = $namespace . '\\Row'; @@ -131,13 +133,9 @@ public function init(): void * Get primary key(s) * * @return array - * @throws InvalidPrimaryKeyException if primary key was not set or has wrong format */ public function getPrimaryKey(): array { - if (!is_array($this->primary)) { - throw new InvalidPrimaryKeyException('The primary key must be set as an array'); - } return $this->primary; } @@ -212,35 +210,34 @@ public static function getColumns(): array * * @return array */ - public static function filterColumns(array $data): array + public function filterColumns(array $data): array { - return array_intersect_key($data, array_flip(static::getColumns())); + return array_intersect_key($data, array_flip($this->getColumns())); } /** * Fetching rows by SQL query * - * @param string $sql SQL query with placeholders + * @param string $sql SQL query with placeholders * @param array $params Params for query placeholders * * @return RowInterface[] of rows results in FETCH_CLASS mode */ - protected static function fetch(string $sql, array $params = []): array + protected function fetch(string $sql, array $params = []): array { - $self = static::getInstance(); - return DbProxy::fetchObjects($sql, $params, $self->rowClass); + return DbProxy::fetchObjects($sql, $params, $this->rowClass); } /** - * {@inheritdoc} + * @inheritDoc * * @throws DbException * @throws InvalidPrimaryKeyException if wrong count of values passed * @throws InvalidArgumentException */ - public static function find(...$keys): array + public function find(...$keys): array { - $keyNames = array_values(static::getInstance()->getPrimaryKey()); + $keyNames = array_values($this->getPrimaryKey()); $whereList = []; foreach ($keys as $keyValues) { @@ -261,19 +258,17 @@ public static function find(...$keys): array $whereList[] = $keyValues; } } - return static::findWhere(...$whereList); + return $this->findWhere(...$whereList); } /** - * {@inheritdoc} + * @inheritDoc * * @throws InvalidArgumentException * @throws Exception\DbException */ - public static function findWhere(...$where): array + public function findWhere(...$where): array { - $self = static::getInstance(); - $whereParams = []; if (count($where) === 2 && is_string($where[0])) { @@ -290,17 +285,17 @@ public static function findWhere(...$where): array $keyValue ); $keyValue = implode(',', $keyValue); - $whereAndTerms[] = $self->name . '.' . $keyName . ' IN (' . $keyValue . ')'; + $whereAndTerms[] = $this->name . '.' . $keyName . ' IN (' . $keyValue . ')'; } elseif (null === $keyValue) { - $whereAndTerms[] = $self->name . '.' . $keyName . ' IS NULL'; + $whereAndTerms[] = $this->name . '.' . $keyName . ' IS NULL'; } elseif ( is_string($keyValue) - && ('%' === substr($keyValue, 0, 1) || '%' === substr($keyValue, -1, 1)) + && (str_starts_with($keyValue, '%') || str_ends_with($keyValue, '%')) ) { - $whereAndTerms[] = $self->name . '.' . $keyName . ' LIKE ?'; + $whereAndTerms[] = $this->name . '.' . $keyName . ' LIKE ?'; $whereParams[] = $keyValue; } else { - $whereAndTerms[] = $self->name . '.' . $keyName . ' = ?'; + $whereAndTerms[] = $this->name . '.' . $keyName . ' = ?'; $whereParams[] = $keyValue; } if (!is_scalar($keyValue) && !is_null($keyValue)) { @@ -319,38 +314,38 @@ public static function findWhere(...$where): array ); } - return self::fetch($self->select . ' WHERE ' . $whereClause, $whereParams); + return self::fetch($this->select . ' WHERE ' . $whereClause, $whereParams); } /** - * {@inheritdoc} + * @inheritDoc * * @throws DbException * @throws InvalidArgumentException * @throws InvalidPrimaryKeyException */ - public static function findRow($primaryKey): ?RowInterface + public function findRow(mixed $primaryKey): ?RowInterface { - $result = static::find($primaryKey); + $result = $this->find($primaryKey); return current($result) ?: null; } /** - * {@inheritdoc} + * @inheritDoc * * @throws DbException * @throws InvalidArgumentException */ - public static function findRowWhere(array $whereList): ?RowInterface + public function findRowWhere(array $whereList): ?RowInterface { - $result = static::findWhere($whereList); + $result = $this->findWhere($whereList); return current($result) ?: null; } /** - * Prepare array for WHERE or SET statements + * Prepare an array for WHERE or SET statements * - * @param array $where + * @param array $where * * @return array */ @@ -384,50 +379,34 @@ private static function prepareStatement(array $where): array * * @return Query\Select */ - public static function select(): Query\Select + public function select(): Query\Select { - $self = static::getInstance(); - $select = new Query\Select(); - $select->select(DbProxy::quoteIdentifier($self->name) . '.*') - ->from($self->name, $self->name) - ->setFetchType($self->rowClass); + $select->select(DbProxy::quoteIdentifier($this->name) . '.*') + ->from($this->name, $this->name) + ->setFetchType($this->rowClass); return $select; } /** - * {@inheritdoc} - */ - public static function create(array $data = []): RowInterface - { - $rowClass = static::getInstance()->rowClass; - /** @var Row $row */ - $row = new $rowClass($data); - $row->setTable(static::getInstance()); - return $row; - } - - /** - * {@inheritdoc} + * @inheritDoc * * @throws Exception\DbException */ - public static function insert(array $data) + public function insert(array $data): ?string { - $self = static::getInstance(); - - $data = static::filterColumns($data); + $data = $this->filterColumns($data); if (!count($data)) { throw new DbException( - "Invalid field names of table `{$self->name}`. Please check use of `insert()` method" + "Invalid field names of table `{$this->name}`. Please check use of `insert()` method" ); } - $table = DbProxy::quoteIdentifier($self->name); + $table = DbProxy::quoteIdentifier($this->name); - $sql = "INSERT INTO $table SET " . implode(',', self::prepareStatement($data)); + $sql = "INSERT INTO $table SET " . implode(',', $this->prepareStatement($data)); $result = DbProxy::query($sql, array_values($data)); if (!$result) { return null; @@ -442,36 +421,34 @@ public static function insert(array $data) * * If the PDO driver does not support this capability, PDO::lastInsertId() triggers an IM001 SQLSTATE. */ - return DbProxy::handler()->lastInsertId($self->sequence); + return DbProxy::handler()->lastInsertId($this->sequence); } /** - * {@inheritdoc} + * @inheritDoc * * @throws Exception\DbException */ - public static function update(array $data, array $where): int + public function update(array $data, array $where): int { - $self = static::getInstance(); - - $data = static::filterColumns($data); + $data = $this->filterColumns($data); if (!count($data)) { throw new DbException( - "Invalid field names of table `{$self->name}`. Please check use of `update()` method" + "Invalid field names of table `{$this->name}`. Please check use of `update()` method" ); } - $where = static::filterColumns($where); + $where = $this->filterColumns($where); if (!count($where)) { throw new DbException( - "Method `Table::update()` can't update all records in the table `{$self->name}`,\n" . + "Method `Table::update()` can't update all records in the table `{$this->name}`,\n" . "please use `Db::query()` instead (of cause if you know what are you doing)" ); } - $table = DbProxy::quoteIdentifier($self->name); + $table = DbProxy::quoteIdentifier($this->name); $sql = "UPDATE $table SET " . implode(',', self::prepareStatement($data)) . " WHERE " . implode(' AND ', self::prepareStatement($where)); @@ -480,11 +457,11 @@ public static function update(array $data, array $where): int } /** - * {@inheritdoc} + * @inheritDoc * * @throws DbException */ - public static function delete(array $where): int + public function delete(array $where): int { $self = static::getInstance(); @@ -496,15 +473,15 @@ public static function delete(array $where): int } - $where = static::filterColumns($where); + $where = $this->filterColumns($where); if (!count($where)) { throw new DbException( - "Invalid field names of table `{$self->name}`. Please check use of `delete()` method" + "Invalid field names of table `{$this->name}`. Please check use of `delete()` method" ); } - $table = DbProxy::quoteIdentifier($self->name); + $table = DbProxy::quoteIdentifier($this->name); $sql = "DELETE FROM $table WHERE " . implode(' AND ', self::prepareStatement($where)); return DbProxy::query($sql, array_values($where)); diff --git a/src/Db/TableInterface.php b/src/Db/TableInterface.php index 88d56c14..6717bdb1 100644 --- a/src/Db/TableInterface.php +++ b/src/Db/TableInterface.php @@ -16,18 +16,11 @@ */ interface TableInterface { - /** - * Get Table instance - * - * @return TableInterface - */ - public static function getInstance(); - /** * Get primary key(s) * * @return array - * @throws InvalidPrimaryKeyException if primary key was not set or has wrong format + * @throws InvalidPrimaryKeyException if the primary key was not set or has a wrong format */ public function getPrimaryKey(): array; @@ -45,15 +38,6 @@ public function getName(): string; */ public function getModel(): string; - /** - * Create Row instance - * - * @param array $data - * - * @return RowInterface - */ - public static function create(array $data = []): RowInterface; - /** * Fetches rows by primary key. The argument specifies one or more primary * key value(s). To find multiple rows by primary key, the argument must @@ -79,11 +63,11 @@ public static function create(array $data = []): RowInterface; * Multiple rows by compound primary key * Table::find([123, 'abc'], [234, 'def'], [345, 'ghi']) * - * @param mixed ...$keys The value(s) of the primary keys. + * @param mixed ...$keys The value(s) of the primary keys. * * @return RowInterface[] */ - public static function find(...$keys): array; + public function find(...$keys): array; /** * Find rows by WHERE @@ -100,29 +84,29 @@ public static function find(...$keys): array; * // WHERE (alias = 'foo' AND userId = 2) OR ('alias' = 'bar' AND userId = 4) * Table::findWhere(['alias'=>'foo', 'userId'=> 2], ['alias'=>'foo', 'userId'=>4]); * - * @param mixed ...$where + * @param mixed ...$where * * @return RowInterface[] */ - public static function findWhere(...$where): array; + public function findWhere(...$where): array; /** * Find row by primary key * - * @param mixed $primaryKey + * @param mixed $primaryKey * - * @return RowInterface + * @return RowInterface|null */ - public static function findRow($primaryKey): ?RowInterface; + public function findRow(mixed $primaryKey): ?RowInterface; /** * Find row by where condition * - * @param array $whereList + * @param array $whereList * - * @return RowInterface + * @return RowInterface|null */ - public static function findRowWhere(array $whereList): ?RowInterface; + public function findRowWhere(array $whereList): ?RowInterface; /** * Prepare Db\Query\Select for current table: @@ -145,45 +129,45 @@ public static function findRowWhere(array $whereList): ?RowInterface; * * @return Query\Select */ - public static function select(): Query\Select; + public function select(): Query\Select; /** * Insert new record to table and return last insert Id * * - * Table::insert(['login' => 'Man', 'email' => 'man@example.com']) + * $table->insert(['login' => 'Man', 'email' => 'man@example.com']) * * - * @param array $data Column-value pairs + * @param array $data Column-value pairs * * @return string|null Primary key or null */ - public static function insert(array $data); + public function insert(array $data): ?string; /** * Updates existing rows * * - * Table::update(['login' => 'Man', 'email' => 'man@domain.com'], ['id' => 42]) + * $table->update(['login' => 'Man', 'email' => 'man@domain.com'], ['id' => 42]) * * - * @param array $data Column-value pairs. - * @param array $where An array of SQL WHERE clause(s) + * @param array $data Column-value pairs. + * @param array $where An array of SQL WHERE clause(s) * * @return integer The number of rows updated */ - public static function update(array $data, array $where): int; + public function update(array $data, array $where): int; /** * Deletes existing rows * * - * Table::delete(['login' => 'Man']) + * $table->deleteWhere(['login' => 'Man']) * * - * @param array $where An array of SQL WHERE clause(s) + * @param array $where An array of SQL WHERE clause(s) * * @return integer The number of rows deleted */ - public static function delete(array $where): int; + public function delete(array $where): int; } diff --git a/src/Db/Traits/RowRelations.php b/src/Db/Traits/RowRelations.php index eb9f00a1..87ed6c0a 100644 --- a/src/Db/Traits/RowRelations.php +++ b/src/Db/Traits/RowRelations.php @@ -28,12 +28,12 @@ trait RowRelations /** * @var array relations rows */ - protected $relations = []; + private array $relations = []; /** * Set relation * - * @param Row $row + * @param Row $row * * @return void * @throws TableNotFoundException @@ -47,9 +47,9 @@ public function setRelation(Row $row): void /** * Get relation by model name * - * @param string $modelName + * @param string $modelName * - * @return RowInterface + * @return RowInterface|null * @throws RelationNotFoundException * @throws TableNotFoundException */ @@ -62,7 +62,7 @@ public function getRelation($modelName): ?RowInterface /** * Get relations by model name * - * @param string $modelName + * @param string $modelName * * @return RowInterface[] * @throws RelationNotFoundException @@ -70,9 +70,7 @@ public function getRelation($modelName): ?RowInterface */ public function getRelations($modelName): array { - if (!isset($this->relations[$modelName])) { - $this->relations[$modelName] = Relations::findRelation($this, $modelName); - } + $this->relations[$modelName] ??= Relations::findRelation($this, $modelName); return $this->relations[$modelName]; } diff --git a/src/Db/Traits/TableProperty.php b/src/Db/Traits/TableProperty.php index 5f024ae1..6d31c3fc 100644 --- a/src/Db/Traits/TableProperty.php +++ b/src/Db/Traits/TableProperty.php @@ -23,20 +23,20 @@ trait TableProperty { /** - * @var TableInterface instance + * @var TableInterface|null instance */ - protected $table; + private ?TableInterface $tableInstance = null; /** * Setup Table instance * - * @param TableInterface $table + * @param TableInterface $table * * @return void */ public function setTable(TableInterface $table): void { - $this->table = $table; + $this->tableInstance = $table; } /** @@ -47,10 +47,10 @@ public function setTable(TableInterface $table): void */ public function getTable(): TableInterface { - if (!$this->table) { + if (!$this->tableInstance) { $this->initTable(); } - return $this->table; + return $this->tableInstance; } /** diff --git a/src/Db/Traits/TableRelations.php b/src/Db/Traits/TableRelations.php index 0e68b57d..650a2c26 100644 --- a/src/Db/Traits/TableRelations.php +++ b/src/Db/Traits/TableRelations.php @@ -24,9 +24,9 @@ trait TableRelations /** * Setup relation "one to one" or "one to many" * - * @param string $key - * @param string $model - * @param string $foreign + * @param string $key + * @param string $model + * @param string $foreign * * @return void */ @@ -39,8 +39,8 @@ public function linkTo($key, $model, $foreign): void * Setup relation "many to many" * [table1-key] [table1_key-table2-table3_key] [table3-key] * - * @param string $model - * @param string $link + * @param string $model + * @param string $link * * @return void */ diff --git a/src/EventManager/Event.php b/src/EventManager/Event.php index 6fbed6c2..82469da4 100644 --- a/src/EventManager/Event.php +++ b/src/EventManager/Event.php @@ -21,30 +21,28 @@ class Event /** * @var string event name */ - protected $name; + protected string $name; /** - * @var string|object the event target + * @var mixed the event target */ - protected $target; + protected mixed $target = null; /** - * @var array|object the event parameters + * @var array the event parameters */ - protected $params = []; + protected array $params = []; /** * Constructor * * Accept a target and its parameters. * - * @param string $name Event name - * @param string|object $target - * @param array|object $params - * - * @throws EventException + * @param string $name Event name + * @param mixed $target + * @param array|null $params */ - public function __construct(string $name, $target = null, $params = null) + public function __construct(string $name, mixed $target = null, array $params = null) { $this->setName($name); @@ -72,9 +70,9 @@ public function getName(): string * * This may be either an object, or the name of a static method. * - * @return string|object + * @return mixed */ - public function getTarget() + public function getTarget(): mixed { return $this->target; } @@ -82,28 +80,21 @@ public function getTarget() /** * Overwrites parameters * - * @param array|object $params + * @param array $params * * @return void - * @throws EventException */ - public function setParams($params): void + public function setParams(array $params): void { - if (!is_array($params) && !is_object($params)) { - throw new EventException( - 'Event parameters must be an array or object; received `' . gettype($params) . '`' - ); - } - $this->params = $params; } /** * Get all parameters * - * @return array|object + * @return array */ - public function getParams() + public function getParams(): array { return $this->params; } @@ -113,23 +104,14 @@ public function getParams() * * If the parameter does not exist, the $default value will be returned. * - * @param string|int $name - * @param mixed $default + * @param int|string $name + * @param mixed|null $default * * @return mixed */ - public function getParam($name, $default = null) + public function getParam(int|string $name, mixed $default = null): mixed { - if (is_array($this->params)) { - // Check in params that are arrays or implement array access - return $this->params[$name] ?? $default; - } elseif (is_object($this->params)) { - // Check in normal objects - return $this->params->{$name} ?? $default; - } else { - // Wrong type, return default value - return $default; - } + return $this->params[$name] ?? $default; } /** @@ -147,11 +129,11 @@ public function setName(string $name): void /** * Set the event target/context * - * @param null|string|object $target + * @param mixed $target * * @return void */ - public function setTarget($target): void + public function setTarget(mixed $target): void { $this->target = $target; } @@ -159,19 +141,13 @@ public function setTarget($target): void /** * Set an individual parameter to a value * - * @param string|int $name - * @param mixed $value + * @param int|string $name + * @param mixed $value * * @return void */ - public function setParam($name, $value): void + public function setParam(int|string $name, mixed $value): void { - if (is_array($this->params)) { - // Arrays or objects implementing array access - $this->params[$name] = $value; - } else { - // Objects - $this->params->{$name} = $value; - } + $this->params[$name] = $value; } } diff --git a/src/EventManager/EventManager.php b/src/EventManager/EventManager.php index 95f7160d..dabd50a5 100644 --- a/src/EventManager/EventManager.php +++ b/src/EventManager/EventManager.php @@ -22,45 +22,40 @@ class EventManager /** * @var array list of listeners */ - protected $listeners = []; + protected array $listeners = []; /** * Attach callback to event * * @param string $eventName * @param callable $callback - * @param integer $priority + * @param int $priority * * @return void */ public function attach(string $eventName, callable $callback, int $priority = 1): void { - if (!isset($this->listeners[$eventName])) { - $this->listeners[$eventName] = []; - } - if (!isset($this->listeners[$eventName][$priority])) { - $this->listeners[$eventName][$priority] = []; - } + $this->listeners[$eventName] ??= []; + $this->listeners[$eventName][$priority] ??= []; $this->listeners[$eventName][$priority][] = $callback; } /** * Trigger event * - * @param string|Event $event - * @param string|object $target - * @param array|object $params + * @param string|Event $event + * @param mixed $target + * @param array|null $params * - * @return string|object - * @throws EventException + * @return mixed */ - public function trigger($event, $target = null, $params = null) + public function trigger(string|Event $event, mixed $target = null, array $params = null): mixed { if (!$event instanceof Event) { $event = new Event($event, $target, $params); } - if (false !== strpos($event->getName(), ':')) { + if (str_contains($event->getName(), ':')) { $namespace = substr($event->getName(), 0, strpos($event->getName(), ':')); if (isset($this->listeners[$namespace])) { diff --git a/src/Grid/Data.php b/src/Grid/Data.php index d25d3f09..129ea707 100644 --- a/src/Grid/Data.php +++ b/src/Grid/Data.php @@ -27,7 +27,7 @@ class Data extends \ArrayIterator /** * Set total rows * - * @param integer $total + * @param int $total * * @return void */ diff --git a/src/Grid/Grid.php b/src/Grid/Grid.php index f4052606..84ee90e5 100644 --- a/src/Grid/Grid.php +++ b/src/Grid/Grid.php @@ -416,7 +416,7 @@ public function setParams($params): void /** * Return params prepared for url builder * - * @param array $rewrite + * @param array $rewrite * * @return array */ @@ -501,7 +501,7 @@ public function addAllowOrder(string $column): void /** * Set allow orders * - * @param string[] $orders + * @param string[] $orders * * @return void */ @@ -572,7 +572,7 @@ public function addOrder(string $column, string $order = self::ORDER_ASC): void /** * Add order rules * - * @param array $orders + * @param array $orders * * @return void * @throws GridException @@ -602,7 +602,7 @@ public function setOrder(string $column, string $order = self::ORDER_ASC): void /** * Set orders * - * @param array $orders + * @param array $orders * * @return void * @throws GridException @@ -642,7 +642,7 @@ public function addAllowFilter(string $column): void /** * Set allowed filters * - * @param string[] $filters + * @param string[] $filters * * @return void */ @@ -707,9 +707,7 @@ public function addFilter(string $column, string $filter, string $value): void if (!$this->checkFilterName($filter)) { throw new GridException('Filter name is incorrect'); } - if (!isset($this->filters[$column])) { - $this->filters[$column] = []; - } + $this->filters[$column] ??= []; $this->filters[$column][$filter] = $value; } @@ -780,7 +778,7 @@ public function applyAlias(string $column): string /** * Set page * - * @param integer $page + * @param int $page * * @return void * @throws GridException @@ -806,7 +804,7 @@ public function getPage(): int /** * Set limit per page * - * @param integer $limit + * @param int $limit * * @return void * @throws GridException @@ -832,7 +830,7 @@ public function getLimit(): int /** * Set default limit * - * @param integer $limit + * @param int $limit * * @return void * @throws GridException diff --git a/src/Grid/Helper/Order.php b/src/Grid/Helper/Order.php index 95502b28..db33aa2a 100644 --- a/src/Grid/Helper/Order.php +++ b/src/Grid/Helper/Order.php @@ -16,7 +16,7 @@ return /** * @param string $column - * @param null $order + * @param null $order * @param string $defaultOrder * @param bool $reset * diff --git a/src/Grid/Source/AbstractSource.php b/src/Grid/Source/AbstractSource.php index 33988aa3..8df3f194 100644 --- a/src/Grid/Source/AbstractSource.php +++ b/src/Grid/Source/AbstractSource.php @@ -44,8 +44,8 @@ abstract class AbstractSource /** * Process source * - * @param int $page - * @param int $limit + * @param int $page + * @param int $limit * @param array $filters * @param array $orders * @@ -56,7 +56,7 @@ abstract public function process(int $page, int $limit, array $filters = [], arr /** * Setup source adapter * - * @param mixed $source + * @param mixed $source * * @return void */ diff --git a/src/Grid/Source/ArraySource.php b/src/Grid/Source/ArraySource.php index 668281af..680ccdce 100644 --- a/src/Grid/Source/ArraySource.php +++ b/src/Grid/Source/ArraySource.php @@ -27,7 +27,7 @@ class ArraySource extends AbstractSource /** * Set array source * - * @param array $source + * @param array $source * * @return void * @throws Grid\GridException @@ -41,7 +41,7 @@ public function setSource($source): void } /** - * {@inheritdoc} + * @inheritDoc * @throws Grid\GridException */ public function process(int $page, int $limit, array $filters = [], array $orders = []): Data @@ -67,7 +67,7 @@ public function process(int $page, int $limit, array $filters = [], array $order /** * Apply filters to array * - * @param array $settings + * @param array $settings * * @return void * @throws Grid\GridException @@ -129,7 +129,7 @@ function ($row) use ($settings) { /** * Apply order to array * - * @param array $settings + * @param array $settings * * @return void * @throws Grid\GridException diff --git a/src/Grid/Source/SelectSource.php b/src/Grid/Source/SelectSource.php index 6840697d..f93b4113 100644 --- a/src/Grid/Source/SelectSource.php +++ b/src/Grid/Source/SelectSource.php @@ -29,10 +29,10 @@ class SelectSource extends AbstractSource /** * Set Select source * - * @param Db\Query\Select $source + * @param Db\Query\Select $source * - * @throws Grid\GridException * @return void + * @throws Grid\GridException */ public function setSource($source): void { @@ -43,7 +43,7 @@ public function setSource($source): void } /** - * {@inheritdoc} + * @inheritDoc * * @throws Grid\GridException * @throws Db\Exception\DbException @@ -98,7 +98,7 @@ function () use (&$data, &$total, $totalSql) { /** * Apply filters to Select * - * @param array $settings + * @param array $settings * * @return void * @throws Grid\GridException @@ -118,7 +118,7 @@ private function applyFilters(array $settings): void /** * Apply order to Select * - * @param array $settings + * @param array $settings * * @return void * @throws Grid\GridException diff --git a/src/Grid/Source/SqlSource.php b/src/Grid/Source/SqlSource.php index 3b4133eb..f1530ab5 100644 --- a/src/Grid/Source/SqlSource.php +++ b/src/Grid/Source/SqlSource.php @@ -30,7 +30,7 @@ class SqlSource extends AbstractSource /** * Set SQL source * - * @param string $source + * @param string $source * * @return void * @throws GridException @@ -44,7 +44,7 @@ public function setSource($source): void } /** - * {@inheritdoc} + * @inheritDoc */ public function process(int $page, int $limit, array $filters = [], array $orders = []): Data { @@ -96,7 +96,7 @@ function () use (&$data, &$total, $dataSql, $totalSql) { /** * Apply filters to SQL query * - * @param array[] $settings + * @param array[] $settings * * @return array */ @@ -119,7 +119,7 @@ private function applyFilters(array $settings): array /** * Apply order to SQL query * - * @param array $settings + * @param array $settings * * @return array */ diff --git a/src/Http/CacheControl.php b/src/Http/CacheControl.php index 581a7bbf..f3bd7730 100644 --- a/src/Http/CacheControl.php +++ b/src/Http/CacheControl.php @@ -11,7 +11,7 @@ namespace Bluz\Http; -use Bluz\Common\Container\Container; +use Bluz\Container\Container; use Bluz\Response\Response; use DateTime; use DateTimeZone; @@ -28,7 +28,6 @@ * - Age * * @package Bluz\Http - * @author Anton Shevchuk * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 */ @@ -39,7 +38,7 @@ class CacheControl /** * @var Response instance */ - protected $response; + protected Response $response; /** * Create instance @@ -110,7 +109,7 @@ public function setPublic(): void * First, it checks for a s-maxage directive, then a max-age directive, and then it falls * back on an expires header. It returns null when no maximum age can be established. * - * @return integer|null Number of seconds + * @return int|null Number of seconds */ public function getMaxAge(): ?int { @@ -124,7 +123,7 @@ public function getMaxAge(): ?int if ($expires = $this->getExpires()) { $expires = DateTime::createFromFormat(DATE_RFC2822, $expires); - return (int) $expires->format('U') - date('U'); + return (int)$expires->format('U') - date('U'); } return null; @@ -135,7 +134,7 @@ public function getMaxAge(): ?int * * This methods sets the Cache-Control max-age directive. * - * @param integer $value Number of seconds + * @param int $value Number of seconds * * @return void */ @@ -150,7 +149,7 @@ public function setMaxAge(int $value): void * * This methods sets the Cache-Control s-maxage directive. * - * @param integer $value Number of seconds + * @param int $value Number of seconds * * @return void */ @@ -168,14 +167,11 @@ public function setSharedMaxAge(int $value): void * When the responses TTL is <= 0, the response may not be served from cache without first * revalidating with the origin. * - * @return integer|null The TTL in seconds + * @return int|null The TTL in seconds */ public function getTtl(): ?int { - if ($maxAge = $this->getMaxAge()) { - return $maxAge - $this->getAge(); - } - return null; + return ($maxAge = $this->getMaxAge()) ? $maxAge - $this->getAge() : null; } /** @@ -183,7 +179,7 @@ public function getTtl(): ?int * * This method adjusts the Cache-Control/s-maxage directive. * - * @param integer $seconds Number of seconds + * @param int $seconds Number of seconds * * @return void */ @@ -197,7 +193,7 @@ public function setTtl(int $seconds): void * * This method adjusts the Cache-Control/max-age directive. * - * @param integer $seconds Number of seconds + * @param int $seconds Number of seconds * * @return void */ @@ -233,7 +229,7 @@ public function setEtag(string $etag, bool $weak = false): void /** * Returns the age of the response * - * @return integer The age of the response in seconds + * @return int The age of the response in seconds */ public function getAge(): int { @@ -246,7 +242,7 @@ public function getAge(): int /** * Set the age of the response * - * @param integer $age + * @param int $age * * @return void */ @@ -273,13 +269,9 @@ public function getExpires(): string * @return void * @throws Exception */ - public function setExpires($date): void + public function setExpires(DateTime|string $date): void { - if ($date instanceof DateTime) { - $date = clone $date; - } else { - $date = new DateTime($date); - } + $date = $date instanceof DateTime ? clone $date : new DateTime($date); $date->setTimezone(new DateTimeZone('UTC')); $this->response->setHeader('Expires', $date->format('D, d M Y H:i:s') . ' GMT'); @@ -298,18 +290,14 @@ public function getLastModified(): string /** * Sets the Last-Modified HTTP header with a DateTime instance or string * - * @param DateTime|string $date A \DateTime instance or date as string + * @param DateTime|string $date A \DateTime instance or date as string * * @return void * @throws Exception */ - public function setLastModified($date): void + public function setLastModified(DateTime|string $date): void { - if ($date instanceof DateTime) { - $date = clone $date; - } else { - $date = new DateTime($date); - } + $date = $date instanceof DateTime ? clone $date : new DateTime($date); $date->setTimezone(new DateTimeZone('UTC')); $this->response->setHeader('Last-Modified', $date->format('D, d M Y H:i:s') . ' GMT'); diff --git a/src/Http/Exception/BadRequestException.php b/src/Http/Exception/BadRequestException.php index d71cfed6..5ce77ae2 100644 --- a/src/Http/Exception/BadRequestException.php +++ b/src/Http/Exception/BadRequestException.php @@ -17,12 +17,8 @@ * BadRequest Exception * * @package Bluz\Http\Exception - * @author Anton Shevchuk */ class BadRequestException extends HttpException { - /** - * @var integer HTTP Code - */ - protected $code = StatusCode::BAD_REQUEST; + protected StatusCode $statusCode = StatusCode::BAD_REQUEST; } diff --git a/src/Http/Exception/ForbiddenException.php b/src/Http/Exception/ForbiddenException.php index e3774e98..9e63ade8 100644 --- a/src/Http/Exception/ForbiddenException.php +++ b/src/Http/Exception/ForbiddenException.php @@ -21,8 +21,5 @@ */ class ForbiddenException extends HttpException { - /** - * @var integer HTTP Code - */ - protected $code = StatusCode::FORBIDDEN; + protected StatusCode $statusCode = StatusCode::FORBIDDEN; } diff --git a/src/Http/Exception/HttpException.php b/src/Http/Exception/HttpException.php index ba9d0c11..a0b11f24 100644 --- a/src/Http/Exception/HttpException.php +++ b/src/Http/Exception/HttpException.php @@ -22,13 +22,37 @@ */ class HttpException extends CommonException { + /** + * @var StatusCode Used as default HTTP code for exceptions + */ + protected StatusCode $statusCode = StatusCode::INTERNAL_SERVER_ERROR; + + public function __construct(string $message = "", int $code = 0, ?\Throwable $previous = null) + { + parent::__construct( + $message ?: $this->statusCode->message(), + $code ?: $this->statusCode->value, + $previous + ); + } + + /** + * Return HTTP Status Message + * + * @return StatusCode + */ + public function getStatusCode(): StatusCode + { + return $this->statusCode; + } + /** * Return HTTP Status Message * * @return string */ - public function getStatus(): string + public function getStatusCodeMessage(): string { - return StatusCode::$statusTexts[$this->code]; + return $this->statusCode->message(); } } diff --git a/src/Http/Exception/NotAcceptableException.php b/src/Http/Exception/NotAcceptableException.php index 006c7472..3c7e8ad5 100644 --- a/src/Http/Exception/NotAcceptableException.php +++ b/src/Http/Exception/NotAcceptableException.php @@ -21,8 +21,5 @@ */ class NotAcceptableException extends HttpException { - /** - * @var integer HTTP Code - */ - protected $code = StatusCode::NOT_ACCEPTABLE; + protected StatusCode $statusCode = StatusCode::NOT_ACCEPTABLE; } diff --git a/src/Http/Exception/NotAllowedException.php b/src/Http/Exception/NotAllowedException.php index 3c834cd3..fb54339a 100644 --- a/src/Http/Exception/NotAllowedException.php +++ b/src/Http/Exception/NotAllowedException.php @@ -21,8 +21,5 @@ */ class NotAllowedException extends HttpException { - /** - * @var integer HTTP Code - */ - protected $code = StatusCode::METHOD_NOT_ALLOWED; + protected StatusCode $statusCode = StatusCode::METHOD_NOT_ALLOWED; } diff --git a/src/Http/Exception/NotFoundException.php b/src/Http/Exception/NotFoundException.php index 64d14332..4cae47b5 100644 --- a/src/Http/Exception/NotFoundException.php +++ b/src/Http/Exception/NotFoundException.php @@ -21,8 +21,5 @@ */ class NotFoundException extends HttpException { - /** - * @var integer HTTP Code - */ - protected $code = StatusCode::NOT_FOUND; + protected StatusCode $statusCode = StatusCode::NOT_FOUND; } diff --git a/src/Http/Exception/NotImplementedException.php b/src/Http/Exception/NotImplementedException.php index 2fed7074..231040d0 100644 --- a/src/Http/Exception/NotImplementedException.php +++ b/src/Http/Exception/NotImplementedException.php @@ -21,8 +21,5 @@ */ class NotImplementedException extends HttpException { - /** - * @var integer HTTP code - */ - protected $code = StatusCode::NOT_IMPLEMENTED; + protected StatusCode $statusCode = StatusCode::NOT_IMPLEMENTED; } diff --git a/src/Http/Exception/RedirectException.php b/src/Http/Exception/RedirectException.php index 9af95753..205be01c 100644 --- a/src/Http/Exception/RedirectException.php +++ b/src/Http/Exception/RedirectException.php @@ -27,22 +27,20 @@ class RedirectException extends HttpException * - 301 Moved Permanently * - 302 Moved Temporarily / Found * - 307 Temporary Redirect - * - * @var integer */ - protected $code = StatusCode::FOUND; + protected StatusCode $statusCode = StatusCode::FOUND; /** * @var string */ - protected $url; + protected string $url; /** * Set Url to Redirect * * @param string $url */ - public function setUrl($url): void + public function setUrl(string $url): void { $this->url = $url; } diff --git a/src/Http/Exception/UnauthorizedException.php b/src/Http/Exception/UnauthorizedException.php index 391d817c..18aff8b4 100644 --- a/src/Http/Exception/UnauthorizedException.php +++ b/src/Http/Exception/UnauthorizedException.php @@ -21,8 +21,5 @@ */ class UnauthorizedException extends HttpException { - /** - * @var integer HTTP code - */ - protected $code = StatusCode::UNAUTHORIZED; + protected StatusCode $statusCode = StatusCode::UNAUTHORIZED; } diff --git a/src/Http/MimeType.php b/src/Http/MimeType.php new file mode 100644 index 00000000..7f0fde6f --- /dev/null +++ b/src/Http/MimeType.php @@ -0,0 +1,30 @@ + 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', // RFC2518 - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', // RFC4918 - 208 => 'Already Reported', // RFC5842 - 226 => 'IM Used', // RFC3229 - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 308 => 'Permanent Redirect', // RFC7238 - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Payload Too Large', - 414 => 'URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Range Not Satisfiable', - 417 => 'Expectation Failed', - 418 => 'I\'m a teapot', // RFC2324 - 421 => 'Misdirected Request', // RFC7540 - 422 => 'Unprocessable Entity', // RFC4918 - 423 => 'Locked', // RFC4918 - 424 => 'Failed Dependency', // RFC4918 - 425 => 'Reserved for WebDAV advanced collections expired proposal', // RFC2817 - 426 => 'Upgrade Required', // RFC2817 - 428 => 'Precondition Required', // RFC6585 - 429 => 'Too Many Requests', // RFC6585 - 431 => 'Request Header Fields Too Large', // RFC6585 - 451 => 'Unavailable For Legal Reasons', // RFC7725 - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported', - 506 => 'Variant Also Negotiates (Experimental)', // RFC2295 - 507 => 'Insufficient Storage', // RFC4918 - 508 => 'Loop Detected', // RFC5842 - 510 => 'Not Extended', // RFC2774 - 511 => 'Network Authentication Required', // RFC6585 - ]; + public function message(): string + { + return match ($this) { + self::CONTINUE => _('Continue'), + self::SWITCHING_PROTOCOLS => _('Switching Protocols'), + self::PROCESSING => _('Processing'), + self::EARLY_HINTS => _('Early Hints'), + self::OK => _('OK'), + self::CREATED => _('Created'), + self::ACCEPTED => _('Accepted'), + self::NON_AUTHORITATIVE_INFORMATION => _('Non-Authoritative Information'), + self::NO_CONTENT => _('No Content'), + self::RESET_CONTENT => _('Reset Content'), + self::PARTIAL_CONTENT => _('Partial Content'), + self::MULTI_STATUS => _('Multi-Status'), + self::ALREADY_REPORTED => _('Already Reported'), + self::IM_USED => _('IM Used'), + self::MULTIPLE_CHOICES => _('Multiple Choices'), + self::MOVED_PERMANENTLY => _('Moved Permanently'), + self::FOUND => _('Found'), + self::SEE_OTHER => _('See Other'), + self::NOT_MODIFIED => _('Not Modified'), + self::USE_PROXY => _('Use Proxy'), + self::SWITCH_PROXY => _('Switch Proxy is a reserved and unused status code'), + self::TEMPORARY_REDIRECT => _('Temporary Redirect'), + self::PERMANENTLY_REDIRECT => _('Permanent Redirect'), + self::BAD_REQUEST => _('Bad Request'), + self::UNAUTHORIZED => _('Unauthorized'), + self::PAYMENT_REQUIRED => _('Payment Required'), + self::FORBIDDEN => _('Forbidden'), + self::NOT_FOUND => _('Not Found'), + self::METHOD_NOT_ALLOWED => _('Method Not Allowed'), + self::NOT_ACCEPTABLE => _('Not Acceptable'), + self::PROXY_AUTHENTICATION_REQUIRED => _('Proxy Authentication Required'), + self::REQUEST_TIMEOUT => _('Request Timeout'), + self::CONFLICT => _('Conflict'), + self::GONE => _('Gone'), + self::LENGTH_REQUIRED => _('Length Required'), + self::PRECONDITION_FAILED => _('Precondition Failed'), + self::REQUEST_ENTITY_TOO_LARGE => _('Payload Too Large'), + self::REQUEST_URI_TOO_LONG => _('URI Too Long'), + self::UNSUPPORTED_MEDIA_TYPE => _('Unsupported Media Type'), + self::REQUESTED_RANGE_NOT_SATISFIABLE => _('Range Not Satisfiable'), + self::EXPECTATION_FAILED => _('Expectation Failed'), + self::I_AM_A_TEAPOT => _('I\'m a teapot'), + self::MISDIRECTED_REQUEST => _('Misdirected Request'), + self::UNPROCESSABLE_ENTITY => _('Unprocessable Entity'), + self::LOCKED => _('Locked'), + self::FAILED_DEPENDENCY => _('Failed Dependency'), + self::TOO_EARLY => _('Too Early'), + self::UPGRADE_REQUIRED => _('Upgrade Required'), + self::PRECONDITION_REQUIRED => _('Precondition Required'), + self::TOO_MANY_REQUESTS => _('Too Many Requests'), + self::REQUEST_HEADER_FIELDS_TOO_LARGE => _('Request Header Fields Too Large'), + self::UNAVAILABLE_FOR_LEGAL_REASONS => _('Unavailable For Legal Reasons'), + self::INTERNAL_SERVER_ERROR => _('Internal Server Error'), + self::NOT_IMPLEMENTED => _('Not Implemented'), + self::BAD_GATEWAY => _('Bad Gateway'), + self::SERVICE_UNAVAILABLE => _('Service Unavailable'), + self::GATEWAY_TIMEOUT => _('Gateway Timeout'), + self::VERSION_NOT_SUPPORTED => _('HTTP Version Not Supported'), + self::VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL => _('Variant Also Negotiates (Experimental)'), + self::INSUFFICIENT_STORAGE => _('Insufficient Storage'), + self::LOOP_DETECTED => _('Loop Detected'), + self::NOT_EXTENDED => _('Not Extended'), + self::NETWORK_AUTHENTICATION_REQUIRED => _('Network Authentication Required'), + }; + } } diff --git a/src/Layout/Helper/BreadCrumbs.php b/src/Layout/Helper/BreadCrumbs.php index be526f66..93b9f3bd 100644 --- a/src/Layout/Helper/BreadCrumbs.php +++ b/src/Layout/Helper/BreadCrumbs.php @@ -16,7 +16,7 @@ /** * Set or Get Breadcrumbs * - * @param array $data + * @param array $data * * @return array|null */ diff --git a/src/Layout/Helper/HeadScript.php b/src/Layout/Helper/HeadScript.php index 9d900f53..1bd25f0e 100644 --- a/src/Layout/Helper/HeadScript.php +++ b/src/Layout/Helper/HeadScript.php @@ -18,7 +18,7 @@ * Set or generate ` - * - * But will *not* be true for strings like: - * - * - `Home` - * - `
Home` - * - `` - * - * For checking the raw source code, use `seeInSource()`. - * - * @param string $text - * @param array|string $selector optional - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::see() - */ - public function canSee($text, $selector = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('see', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page contains the given string (case insensitive). - * - * You can specify a specific HTML element (via CSS or XPath) as the second - * parameter to only search within that element. - * - * ``` php - * see('Logout'); // I can suppose user is logged in - * $I->see('Sign Up', 'h1'); // I can suppose it's a signup page - * $I->see('Sign Up', '//body/h1'); // with XPath - * $I->see('Sign Up', ['css' => 'body h1']); // with strict CSS locator - * ``` - * - * Note that the search is done after stripping all HTML tags from the body, - * so `$I->see('strong')` will return true for strings like: - * - * - `

I am Stronger than thou

` - * - `` - * - * But will *not* be true for strings like: - * - * - `Home` - * - `
Home` - * - `` - * - * For checking the raw source code, use `seeInSource()`. - * - * @param string $text - * @param array|string $selector optional - * @see \Codeception\Lib\InnerBrowser::see() - */ - public function see($text, $selector = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('see', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page doesn't contain the text specified (case insensitive). - * Give a locator as the second parameter to match a specific region. - * - * ```php - * dontSee('Login'); // I can suppose user is already logged in - * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page - * $I->dontSee('Sign Up','//body/h1'); // with XPath - * $I->dontSee('Sign Up', ['css' => 'body h1']); // with strict CSS locator - * ``` - * - * Note that the search is done after stripping all HTML tags from the body, - * so `$I->dontSee('strong')` will fail on strings like: - * - * - `

I am Stronger than thou

` - * - `` - * - * But will ignore strings like: - * - * - `Home` - * - `
Home` - * - `` - * - * For checking the raw source code, use `seeInSource()`. - * - * @param string $text - * @param array|string $selector optional - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSee() - */ - public function cantSee($text, $selector = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSee', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page doesn't contain the text specified (case insensitive). - * Give a locator as the second parameter to match a specific region. - * - * ```php - * dontSee('Login'); // I can suppose user is already logged in - * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page - * $I->dontSee('Sign Up','//body/h1'); // with XPath - * $I->dontSee('Sign Up', ['css' => 'body h1']); // with strict CSS locator - * ``` - * - * Note that the search is done after stripping all HTML tags from the body, - * so `$I->dontSee('strong')` will fail on strings like: - * - * - `

I am Stronger than thou

` - * - `` - * - * But will ignore strings like: - * - * - `Home` - * - `
Home` - * - `` - * - * For checking the raw source code, use `seeInSource()`. - * - * @param string $text - * @param array|string $selector optional - * @see \Codeception\Lib\InnerBrowser::dontSee() - */ - public function dontSee($text, $selector = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSee', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page contains the given string in its - * raw source code. - * - * ``` php - * seeInSource('

Green eggs & ham

'); - * ``` - * - * @param $raw - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInSource() - */ - public function canSeeInSource($raw) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInSource', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page contains the given string in its - * raw source code. - * - * ``` php - * seeInSource('

Green eggs & ham

'); - * ``` - * - * @param $raw - * @see \Codeception\Lib\InnerBrowser::seeInSource() - */ - public function seeInSource($raw) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInSource', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page contains the given string in its - * raw source code. - * - * ```php - * dontSeeInSource('

Green eggs & ham

'); - * ``` - * - * @param $raw - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInSource() - */ - public function cantSeeInSource($raw) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInSource', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current page contains the given string in its - * raw source code. - * - * ```php - * dontSeeInSource('

Green eggs & ham

'); - * ``` - * - * @param $raw - * @see \Codeception\Lib\InnerBrowser::dontSeeInSource() - */ - public function dontSeeInSource($raw) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInSource', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there's a link with the specified text. - * Give a full URL as the second parameter to match links with that exact URL. - * - * ``` php - * seeLink('Logout'); // matches Logout - * $I->seeLink('Logout','/logout'); // matches Logout - * ?> - * ``` - * - * @param string $text - * @param string $url optional - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeLink() - */ - public function canSeeLink($text, $url = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeLink', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there's a link with the specified text. - * Give a full URL as the second parameter to match links with that exact URL. - * - * ``` php - * seeLink('Logout'); // matches Logout - * $I->seeLink('Logout','/logout'); // matches Logout - * ?> - * ``` - * - * @param string $text - * @param string $url optional - * @see \Codeception\Lib\InnerBrowser::seeLink() - */ - public function seeLink($text, $url = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeLink', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page doesn't contain a link with the given string. - * If the second parameter is given, only links with a matching "href" attribute will be checked. - * - * ``` php - * dontSeeLink('Logout'); // I suppose user is not logged in - * $I->dontSeeLink('Checkout now', '/store/cart.php'); - * ?> - * ``` - * - * @param string $text - * @param string $url optional - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeLink() - */ - public function cantSeeLink($text, $url = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeLink', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page doesn't contain a link with the given string. - * If the second parameter is given, only links with a matching "href" attribute will be checked. - * - * ``` php - * dontSeeLink('Logout'); // I suppose user is not logged in - * $I->dontSeeLink('Checkout now', '/store/cart.php'); - * ?> - * ``` - * - * @param string $text - * @param string $url optional - * @see \Codeception\Lib\InnerBrowser::dontSeeLink() - */ - public function dontSeeLink($text, $url = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeLink', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current URI contains the given string. - * - * ``` php - * seeInCurrentUrl('home'); - * // to match: /users/1 - * $I->seeInCurrentUrl('/users/'); - * ?> - * ``` - * - * @param string $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl() - */ - public function canSeeInCurrentUrl($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInCurrentUrl', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current URI contains the given string. - * - * ``` php - * seeInCurrentUrl('home'); - * // to match: /users/1 - * $I->seeInCurrentUrl('/users/'); - * ?> - * ``` - * - * @param string $uri - * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl() - */ - public function seeInCurrentUrl($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInCurrentUrl', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URI doesn't contain the given string. - * - * ``` php - * dontSeeInCurrentUrl('/users/'); - * ?> - * ``` - * - * @param string $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl() - */ - public function cantSeeInCurrentUrl($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInCurrentUrl', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URI doesn't contain the given string. - * - * ``` php - * dontSeeInCurrentUrl('/users/'); - * ?> - * ``` - * - * @param string $uri - * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl() - */ - public function dontSeeInCurrentUrl($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInCurrentUrl', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL is equal to the given string. - * Unlike `seeInCurrentUrl`, this only matches the full URL. - * - * ``` php - * seeCurrentUrlEquals('/'); - * ?> - * ``` - * - * @param string $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals() - */ - public function canSeeCurrentUrlEquals($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlEquals', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL is equal to the given string. - * Unlike `seeInCurrentUrl`, this only matches the full URL. - * - * ``` php - * seeCurrentUrlEquals('/'); - * ?> - * ``` - * - * @param string $uri - * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals() - */ - public function seeCurrentUrlEquals($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentUrlEquals', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL doesn't equal the given string. - * Unlike `dontSeeInCurrentUrl`, this only matches the full URL. - * - * ``` php - * dontSeeCurrentUrlEquals('/'); - * ?> - * ``` - * - * @param string $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals() - */ - public function cantSeeCurrentUrlEquals($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlEquals', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL doesn't equal the given string. - * Unlike `dontSeeInCurrentUrl`, this only matches the full URL. - * - * ``` php - * dontSeeCurrentUrlEquals('/'); - * ?> - * ``` - * - * @param string $uri - * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals() - */ - public function dontSeeCurrentUrlEquals($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlEquals', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL matches the given regular expression. - * - * ``` php - * seeCurrentUrlMatches('~^/users/(\d+)~'); - * ?> - * ``` - * - * @param string $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches() - */ - public function canSeeCurrentUrlMatches($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlMatches', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the current URL matches the given regular expression. - * - * ``` php - * seeCurrentUrlMatches('~^/users/(\d+)~'); - * ?> - * ``` - * - * @param string $uri - * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches() - */ - public function seeCurrentUrlMatches($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentUrlMatches', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url doesn't match the given regular expression. - * - * ``` php - * dontSeeCurrentUrlMatches('~^/users/(\d+)~'); - * ?> - * ``` - * - * @param string $uri - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches() - */ - public function cantSeeCurrentUrlMatches($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlMatches', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url doesn't match the given regular expression. - * - * ``` php - * dontSeeCurrentUrlMatches('~^/users/(\d+)~'); - * ?> - * ``` - * - * @param string $uri - * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches() - */ - public function dontSeeCurrentUrlMatches($uri) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlMatches', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Executes the given regular expression against the current URI and returns the first capturing group. - * If no parameters are provided, the full URI is returned. - * - * ``` php - * grabFromCurrentUrl('~^/user/(\d+)/~'); - * $uri = $I->grabFromCurrentUrl(); - * ?> - * ``` - * - * @param string $uri optional - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::grabFromCurrentUrl() - */ - public function grabFromCurrentUrl($uri = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabFromCurrentUrl', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the specified checkbox is checked. - * - * ``` php - * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. - * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); - * ?> - * ``` - * - * @param $checkbox - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked() - */ - public function canSeeCheckboxIsChecked($checkbox) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCheckboxIsChecked', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the specified checkbox is checked. - * - * ``` php - * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. - * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); - * ?> - * ``` - * - * @param $checkbox - * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked() - */ - public function seeCheckboxIsChecked($checkbox) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCheckboxIsChecked', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Check that the specified checkbox is unchecked. - * - * ``` php - * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. - * ?> - * ``` - * - * @param $checkbox - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked() - */ - public function cantSeeCheckboxIsChecked($checkbox) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCheckboxIsChecked', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Check that the specified checkbox is unchecked. - * - * ``` php - * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. - * ?> - * ``` - * - * @param $checkbox - * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked() - */ - public function dontSeeCheckboxIsChecked($checkbox) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCheckboxIsChecked', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given input field or textarea *equals* (i.e. not just contains) the given value. - * Fields are matched by label text, the "name" attribute, CSS, or XPath. - * - * ``` php - * seeInField('Body','Type your comment here'); - * $I->seeInField('form textarea[name=body]','Type your comment here'); - * $I->seeInField('form input[type=hidden]','hidden_value'); - * $I->seeInField('#searchform input','Search'); - * $I->seeInField('//form/*[@name=search]','Search'); - * $I->seeInField(['name' => 'search'], 'Search'); - * ?> - * ``` - * - * @param $field - * @param $value - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInField() - */ - public function canSeeInField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInField', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given input field or textarea *equals* (i.e. not just contains) the given value. - * Fields are matched by label text, the "name" attribute, CSS, or XPath. - * - * ``` php - * seeInField('Body','Type your comment here'); - * $I->seeInField('form textarea[name=body]','Type your comment here'); - * $I->seeInField('form input[type=hidden]','hidden_value'); - * $I->seeInField('#searchform input','Search'); - * $I->seeInField('//form/*[@name=search]','Search'); - * $I->seeInField(['name' => 'search'], 'Search'); - * ?> - * ``` - * - * @param $field - * @param $value - * @see \Codeception\Lib\InnerBrowser::seeInField() - */ - public function seeInField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInField', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that an input field or textarea doesn't contain the given value. - * For fuzzy locators, the field is matched by label text, CSS and XPath. - * - * ``` php - * dontSeeInField('Body','Type your comment here'); - * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); - * $I->dontSeeInField('form input[type=hidden]','hidden_value'); - * $I->dontSeeInField('#searchform input','Search'); - * $I->dontSeeInField('//form/*[@name=search]','Search'); - * $I->dontSeeInField(['name' => 'search'], 'Search'); - * ?> - * ``` - * - * @param $field - * @param $value - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInField() - */ - public function cantSeeInField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInField', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that an input field or textarea doesn't contain the given value. - * For fuzzy locators, the field is matched by label text, CSS and XPath. - * - * ``` php - * dontSeeInField('Body','Type your comment here'); - * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); - * $I->dontSeeInField('form input[type=hidden]','hidden_value'); - * $I->dontSeeInField('#searchform input','Search'); - * $I->dontSeeInField('//form/*[@name=search]','Search'); - * $I->dontSeeInField(['name' => 'search'], 'Search'); - * ?> - * ``` - * - * @param $field - * @param $value - * @see \Codeception\Lib\InnerBrowser::dontSeeInField() - */ - public function dontSeeInField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInField', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are set on the form matched with the - * passed selector. - * - * ``` php - * seeInFormFields('form[name=myform]', [ - * 'input1' => 'value', - * 'input2' => 'other value', - * ]); - * ?> - * ``` - * - * For multi-select elements, or to check values of multiple elements with the same name, an - * array may be passed: - * - * ``` php - * seeInFormFields('.form-class', [ - * 'multiselect' => [ - * 'value1', - * 'value2', - * ], - * 'checkbox[]' => [ - * 'a checked value', - * 'another checked value', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * seeInFormFields('#form-id', [ - * 'checkbox1' => true, // passes if checked - * 'checkbox2' => false, // passes if unchecked - * ]); - * ?> - * ``` - * - * Pair this with submitForm for quick testing magic. - * - * ``` php - * 'value', - * 'field2' => 'another value', - * 'checkbox1' => true, - * // ... - * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); - * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInFormFields() - */ - public function canSeeInFormFields($formSelector, $params) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInFormFields', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are set on the form matched with the - * passed selector. - * - * ``` php - * seeInFormFields('form[name=myform]', [ - * 'input1' => 'value', - * 'input2' => 'other value', - * ]); - * ?> - * ``` - * - * For multi-select elements, or to check values of multiple elements with the same name, an - * array may be passed: - * - * ``` php - * seeInFormFields('.form-class', [ - * 'multiselect' => [ - * 'value1', - * 'value2', - * ], - * 'checkbox[]' => [ - * 'a checked value', - * 'another checked value', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * seeInFormFields('#form-id', [ - * 'checkbox1' => true, // passes if checked - * 'checkbox2' => false, // passes if unchecked - * ]); - * ?> - * ``` - * - * Pair this with submitForm for quick testing magic. - * - * ``` php - * 'value', - * 'field2' => 'another value', - * 'checkbox1' => true, - * // ... - * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); - * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * @see \Codeception\Lib\InnerBrowser::seeInFormFields() - */ - public function seeInFormFields($formSelector, $params) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInFormFields', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are not set on the form matched with - * the passed selector. - * - * ``` php - * dontSeeInFormFields('form[name=myform]', [ - * 'input1' => 'non-existent value', - * 'input2' => 'other non-existent value', - * ]); - * ?> - * ``` - * - * To check that an element hasn't been assigned any one of many values, an array can be passed - * as the value: - * - * ``` php - * dontSeeInFormFields('.form-class', [ - * 'fieldName' => [ - * 'This value shouldn\'t be set', - * 'And this value shouldn\'t be set', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * dontSeeInFormFields('#form-id', [ - * 'checkbox1' => true, // fails if checked - * 'checkbox2' => false, // fails if unchecked - * ]); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields() - */ - public function cantSeeInFormFields($formSelector, $params) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInFormFields', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks if the array of form parameters (name => value) are not set on the form matched with - * the passed selector. - * - * ``` php - * dontSeeInFormFields('form[name=myform]', [ - * 'input1' => 'non-existent value', - * 'input2' => 'other non-existent value', - * ]); - * ?> - * ``` - * - * To check that an element hasn't been assigned any one of many values, an array can be passed - * as the value: - * - * ``` php - * dontSeeInFormFields('.form-class', [ - * 'fieldName' => [ - * 'This value shouldn\'t be set', - * 'And this value shouldn\'t be set', - * ], - * ]); - * ?> - * ``` - * - * Additionally, checkbox values can be checked with a boolean. - * - * ``` php - * dontSeeInFormFields('#form-id', [ - * 'checkbox1' => true, // fails if checked - * 'checkbox2' => false, // fails if unchecked - * ]); - * ?> - * ``` - * - * @param $formSelector - * @param $params - * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields() - */ - public function dontSeeInFormFields($formSelector, $params) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInFormFields', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Submits the given form on the page, with the given form - * values. Pass the form field's values as an array in the second - * parameter. - * - * Although this function can be used as a short-hand version of - * `fillField()`, `selectOption()`, `click()` etc. it has some important - * differences: - * - * * Only field *names* may be used, not CSS/XPath selectors nor field labels - * * If a field is sent to this function that does *not* exist on the page, - * it will silently be added to the HTTP request. This is helpful for testing - * some types of forms, but be aware that you will *not* get an exception - * like you would if you called `fillField()` or `selectOption()` with - * a missing field. - * - * Fields that are not provided will be filled by their values from the page, - * or from any previous calls to `fillField()`, `selectOption()` etc. - * You don't need to click the 'Submit' button afterwards. - * This command itself triggers the request to form's action. - * - * You can optionally specify which button's value to include - * in the request with the last parameter (as an alternative to - * explicitly setting its value in the second parameter), as - * button values are not otherwise included in the request. - * - * Examples: - * - * ``` php - * submitForm('#login', [ - * 'login' => 'davert', - * 'password' => '123456' - * ]); - * // or - * $I->submitForm('#login', [ - * 'login' => 'davert', - * 'password' => '123456' - * ], 'submitButtonName'); - * - * ``` - * - * For example, given this sample "Sign Up" form: - * - * ``` html - *
- * Login: - *
- * Password: - *
- * Do you agree to our terms? - *
- * Select pricing plan: - * - * - *
- * ``` - * - * You could write the following to submit it: - * - * ``` php - * submitForm( - * '#userForm', - * [ - * 'user' => [ - * 'login' => 'Davert', - * 'password' => '123456', - * 'agree' => true - * ] - * ], - * 'submitButton' - * ); - * ``` - * Note that "2" will be the submitted value for the "plan" field, as it is - * the selected option. - * - * You can also emulate a JavaScript submission by not specifying any - * buttons in the third parameter to submitForm. - * - * ```php - * submitForm( - * '#userForm', - * [ - * 'user' => [ - * 'login' => 'Davert', - * 'password' => '123456', - * 'agree' => true - * ] - * ] - * ); - * ``` - * - * This function works well when paired with `seeInFormFields()` - * for quickly testing CRUD interfaces and form validation logic. - * - * ``` php - * 'value', - * 'field2' => 'another value', - * 'checkbox1' => true, - * // ... - * ]; - * $I->submitForm('#my-form', $form, 'submitButton'); - * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('#my-form', $form); - * ``` - * - * Parameter values can be set to arrays for multiple input fields - * of the same name, or multi-select combo boxes. For checkboxes, - * you can use either the string value or boolean `true`/`false` which will - * be replaced by the checkbox's value in the DOM. - * - * ``` php - * submitForm('#my-form', [ - * 'field1' => 'value', - * 'checkbox' => [ - * 'value of first checkbox', - * 'value of second checkbox', - * ], - * 'otherCheckboxes' => [ - * true, - * false, - * false - * ], - * 'multiselect' => [ - * 'first option value', - * 'second option value' - * ] - * ]); - * ``` - * - * Mixing string and boolean values for a checkbox's value is not supported - * and may produce unexpected results. - * - * Field names ending in `[]` must be passed without the trailing square - * bracket characters, and must contain an array for its value. This allows - * submitting multiple values with the same name, consider: - * - * ```php - * submitForm('#my-form', [ - * 'field[]' => 'value', - * 'field[]' => 'another value', // 'field[]' is already a defined key - * ]); - * ``` - * - * The solution is to pass an array value: - * - * ```php - * submitForm('#my-form', [ - * 'field' => [ - * 'value', - * 'another value', - * ] - * ]); - * ``` - * - * @param $selector - * @param $params - * @param $button - * @see \Codeception\Lib\InnerBrowser::submitForm() - */ - public function submitForm($selector, $params, $button = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('submitForm', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Fills a text field or textarea with the given string. - * - * ``` php - * fillField("//input[@type='text']", "Hello World!"); - * $I->fillField(['name' => 'email'], 'jon@mail.com'); - * ?> - * ``` - * - * @param $field - * @param $value - * @see \Codeception\Lib\InnerBrowser::fillField() - */ - public function fillField($field, $value) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('fillField', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Selects an option in a select tag or in radio button group. - * - * ``` php - * selectOption('form select[name=account]', 'Premium'); - * $I->selectOption('form input[name=payment]', 'Monthly'); - * $I->selectOption('//form/select[@name=account]', 'Monthly'); - * ?> - * ``` - * - * Provide an array for the second argument to select multiple options: - * - * ``` php - * selectOption('Which OS do you use?', array('Windows','Linux')); - * ?> - * ``` - * - * Or provide an associative array for the second argument to specifically define which selection method should be used: - * - * ``` php - * selectOption('Which OS do you use?', array('text' => 'Windows')); // Only search by text 'Windows' - * $I->selectOption('Which OS do you use?', array('value' => 'windows')); // Only search by value 'windows' - * ?> - * ``` - * - * @param $select - * @param $option - * @see \Codeception\Lib\InnerBrowser::selectOption() - */ - public function selectOption($select, $option) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('selectOption', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Ticks a checkbox. For radio buttons, use the `selectOption` method instead. - * - * ``` php - * checkOption('#agree'); - * ?> - * ``` - * - * @param $option - * @see \Codeception\Lib\InnerBrowser::checkOption() - */ - public function checkOption($option) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('checkOption', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Unticks a checkbox. - * - * ``` php - * uncheckOption('#notify'); - * ?> - * ``` - * - * @param $option - * @see \Codeception\Lib\InnerBrowser::uncheckOption() - */ - public function uncheckOption($option) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('uncheckOption', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Attaches a file relative to the Codeception `_data` directory to the given file upload field. - * - * ``` php - * attachFile('input[@type="file"]', 'prices.xls'); - * ?> - * ``` - * - * @param $field - * @param $filename - * @see \Codeception\Lib\InnerBrowser::attachFile() - */ - public function attachFile($field, $filename) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('attachFile', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * If your page triggers an ajax request, you can perform it manually. - * This action sends a GET ajax request with specified params. - * - * See ->sendAjaxPostRequest for examples. - * - * @param $uri - * @param $params - * @see \Codeception\Lib\InnerBrowser::sendAjaxGetRequest() - */ - public function sendAjaxGetRequest($uri, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('sendAjaxGetRequest', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * If your page triggers an ajax request, you can perform it manually. - * This action sends a POST ajax request with specified params. - * Additional params can be passed as array. - * - * Example: - * - * Imagine that by clicking checkbox you trigger ajax request which updates user settings. - * We emulate that click by running this ajax request manually. - * - * ``` php - * sendAjaxPostRequest('/updateSettings', array('notifications' => true)); // POST - * $I->sendAjaxGetRequest('/updateSettings', array('notifications' => true)); // GET - * - * ``` - * - * @param $uri - * @param $params - * @see \Codeception\Lib\InnerBrowser::sendAjaxPostRequest() - */ - public function sendAjaxPostRequest($uri, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('sendAjaxPostRequest', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * If your page triggers an ajax request, you can perform it manually. - * This action sends an ajax request with specified method and params. - * - * Example: - * - * You need to perform an ajax request specifying the HTTP method. - * - * ``` php - * sendAjaxRequest('PUT', '/posts/7', array('title' => 'new title')); - * - * ``` - * - * @param $method - * @param $uri - * @param $params - * @see \Codeception\Lib\InnerBrowser::sendAjaxRequest() - */ - public function sendAjaxRequest($method, $uri, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('sendAjaxRequest', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Finds and returns the text contents of the given element. - * If a fuzzy locator is used, the element is found using CSS, XPath, - * and by matching the full page source by regular expression. - * - * ``` php - * grabTextFrom('h1'); - * $heading = $I->grabTextFrom('descendant-or-self::h1'); - * $value = $I->grabTextFrom('~ - * ``` - * - * @param $cssOrXPathOrRegex - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::grabTextFrom() - */ - public function grabTextFrom($cssOrXPathOrRegex) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabTextFrom', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Grabs the value of the given attribute value from the given element. - * Fails if element is not found. - * - * ``` php - * grabAttributeFrom('#tooltip', 'title'); - * ?> - * ``` - * - * - * @param $cssOrXpath - * @param $attribute - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::grabAttributeFrom() - */ - public function grabAttributeFrom($cssOrXpath, $attribute) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabAttributeFrom', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Grabs either the text content, or attribute values, of nodes - * matched by $cssOrXpath and returns them as an array. - * - * ```html - * First - * Second - * Third - * ``` - * - * ```php - * grabMultiple('a'); - * - * // would return ['#first', '#second', '#third'] - * $aLinks = $I->grabMultiple('a', 'href'); - * ?> - * ``` - * - * @param $cssOrXpath - * @param $attribute - * @return string[] - * @see \Codeception\Lib\InnerBrowser::grabMultiple() - */ - public function grabMultiple($cssOrXpath, $attribute = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabMultiple', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * @param $field - * - * @return array|mixed|null|string - * @see \Codeception\Lib\InnerBrowser::grabValueFrom() - */ - public function grabValueFrom($field) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabValueFrom', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Sets a cookie with the given name and value. - * You can set additional cookie params like `domain`, `path`, `expires`, `secure` in array passed as last argument. - * - * ``` php - * setCookie('PHPSESSID', 'el4ukv0kqbvoirg7nkp4dncpk3'); - * ?> - * ``` - * - * @param $name - * @param $val - * @param array $params - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::setCookie() - */ - public function setCookie($name, $val, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('setCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Grabs a cookie value. - * You can set additional cookie params like `domain`, `path` in array passed as last argument. - * - * @param $cookie - * - * @param array $params - * @return mixed - * @see \Codeception\Lib\InnerBrowser::grabCookie() - */ - public function grabCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Grabs current page source code. - * - * @throws ModuleException if no page was opened. - * - * @return string Current page source code. - * @see \Codeception\Lib\InnerBrowser::grabPageSource() - */ - public function grabPageSource() { - return $this->getScenario()->runStep(new \Codeception\Step\Action('grabPageSource', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that a cookie with the given name is set. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. - * - * ``` php - * seeCookie('PHPSESSID'); - * ?> - * ``` - * - * @param $cookie - * @param array $params - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeCookie() - */ - public function canSeeCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCookie', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that a cookie with the given name is set. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. - * - * ``` php - * seeCookie('PHPSESSID'); - * ?> - * ``` - * - * @param $cookie - * @param array $params - * @return mixed - * @see \Codeception\Lib\InnerBrowser::seeCookie() - */ - public function seeCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there isn't a cookie with the given name. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. - * - * @param $cookie - * - * @param array $params - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeCookie() - */ - public function cantSeeCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCookie', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there isn't a cookie with the given name. - * You can set additional cookie params like `domain`, `path` as array passed in last argument. - * - * @param $cookie - * - * @param array $params - * @return mixed - * @see \Codeception\Lib\InnerBrowser::dontSeeCookie() - */ - public function dontSeeCookie($cookie, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Unsets cookie with the given name. - * You can set additional cookie params like `domain`, `path` in array passed as last argument. - * - * @param $cookie - * - * @param array $params - * @return mixed - * @see \Codeception\Lib\InnerBrowser::resetCookie() - */ - public function resetCookie($name, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Action('resetCookie', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given element exists on the page and is visible. - * You can also specify expected attributes of this element. - * - * ``` php - * seeElement('.error'); - * $I->seeElement('//form/input[1]'); - * $I->seeElement('input', ['name' => 'login']); - * $I->seeElement('input', ['value' => '123456']); - * - * // strict locator in first arg, attributes in second - * $I->seeElement(['css' => 'form input'], ['name' => 'login']); - * ?> - * ``` - * - * @param $selector - * @param array $attributes - * @return - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeElement() - */ - public function canSeeElement($selector, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeElement', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given element exists on the page and is visible. - * You can also specify expected attributes of this element. - * - * ``` php - * seeElement('.error'); - * $I->seeElement('//form/input[1]'); - * $I->seeElement('input', ['name' => 'login']); - * $I->seeElement('input', ['value' => '123456']); - * - * // strict locator in first arg, attributes in second - * $I->seeElement(['css' => 'form input'], ['name' => 'login']); - * ?> - * ``` - * - * @param $selector - * @param array $attributes - * @return - * @see \Codeception\Lib\InnerBrowser::seeElement() - */ - public function seeElement($selector, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeElement', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given element is invisible or not present on the page. - * You can also specify expected attributes of this element. - * - * ``` php - * dontSeeElement('.error'); - * $I->dontSeeElement('//form/input[1]'); - * $I->dontSeeElement('input', ['name' => 'login']); - * $I->dontSeeElement('input', ['value' => '123456']); - * ?> - * ``` - * - * @param $selector - * @param array $attributes - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeElement() - */ - public function cantSeeElement($selector, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeElement', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given element is invisible or not present on the page. - * You can also specify expected attributes of this element. - * - * ``` php - * dontSeeElement('.error'); - * $I->dontSeeElement('//form/input[1]'); - * $I->dontSeeElement('input', ['name' => 'login']); - * $I->dontSeeElement('input', ['value' => '123456']); - * ?> - * ``` - * - * @param $selector - * @param array $attributes - * @see \Codeception\Lib\InnerBrowser::dontSeeElement() - */ - public function dontSeeElement($selector, $attributes = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeElement', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there are a certain number of elements matched by the given locator on the page. - * - * ``` php - * seeNumberOfElements('tr', 10); - * $I->seeNumberOfElements('tr', [0,10]); // between 0 and 10 elements - * ?> - * ``` - * @param $selector - * @param mixed $expected int or int[] - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeNumberOfElements() - */ - public function canSeeNumberOfElements($selector, $expected) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeNumberOfElements', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that there are a certain number of elements matched by the given locator on the page. - * - * ``` php - * seeNumberOfElements('tr', 10); - * $I->seeNumberOfElements('tr', [0,10]); // between 0 and 10 elements - * ?> - * ``` - * @param $selector - * @param mixed $expected int or int[] - * @see \Codeception\Lib\InnerBrowser::seeNumberOfElements() - */ - public function seeNumberOfElements($selector, $expected) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeNumberOfElements', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given option is selected. - * - * ``` php - * seeOptionIsSelected('#form input[name=payment]', 'Visa'); - * ?> - * ``` - * - * @param $selector - * @param $optionText - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeOptionIsSelected() - */ - public function canSeeOptionIsSelected($selector, $optionText) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeOptionIsSelected', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given option is selected. - * - * ``` php - * seeOptionIsSelected('#form input[name=payment]', 'Visa'); - * ?> - * ``` - * - * @param $selector - * @param $optionText - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::seeOptionIsSelected() - */ - public function seeOptionIsSelected($selector, $optionText) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeOptionIsSelected', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given option is not selected. - * - * ``` php - * dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); - * ?> - * ``` - * - * @param $selector - * @param $optionText - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeOptionIsSelected() - */ - public function cantSeeOptionIsSelected($selector, $optionText) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeOptionIsSelected', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the given option is not selected. - * - * ``` php - * dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); - * ?> - * ``` - * - * @param $selector - * @param $optionText - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::dontSeeOptionIsSelected() - */ - public function dontSeeOptionIsSelected($selector, $optionText) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeOptionIsSelected', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that current page has 404 response status code. - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seePageNotFound() - */ - public function canSeePageNotFound() { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seePageNotFound', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Asserts that current page has 404 response status code. - * @see \Codeception\Lib\InnerBrowser::seePageNotFound() - */ - public function seePageNotFound() { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seePageNotFound', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that response code is equal to value provided. - * - * ```php - * seeResponseCodeIs(200); - * - * // recommended \Codeception\Util\HttpCode - * $I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); - * ``` - * - * @param $code - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIs() - */ - public function canSeeResponseCodeIs($code) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIs', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that response code is equal to value provided. - * - * ```php - * seeResponseCodeIs(200); - * - * // recommended \Codeception\Util\HttpCode - * $I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); - * ``` - * - * @param $code - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIs() - */ - public function seeResponseCodeIs($code) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeResponseCodeIs', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that response code is between a certain range. Between actually means [from <= CODE <= to] - * - * @param $from - * @param $to - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIsBetween() - */ - public function canSeeResponseCodeIsBetween($from, $to) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIsBetween', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that response code is between a certain range. Between actually means [from <= CODE <= to] - * - * @param $from - * @param $to - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIsBetween() - */ - public function seeResponseCodeIsBetween($from, $to) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeResponseCodeIsBetween', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that response code is equal to value provided. - * - * ```php - * dontSeeResponseCodeIs(200); - * - * // recommended \Codeception\Util\HttpCode - * $I->dontSeeResponseCodeIs(\Codeception\Util\HttpCode::OK); - * ``` - * @param $code - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeResponseCodeIs() - */ - public function cantSeeResponseCodeIs($code) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeResponseCodeIs', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that response code is equal to value provided. - * - * ```php - * dontSeeResponseCodeIs(200); - * - * // recommended \Codeception\Util\HttpCode - * $I->dontSeeResponseCodeIs(\Codeception\Util\HttpCode::OK); - * ``` - * @param $code - * @see \Codeception\Lib\InnerBrowser::dontSeeResponseCodeIs() - */ - public function dontSeeResponseCodeIs($code) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeResponseCodeIs', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the response code 2xx - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIsSuccessful() - */ - public function canSeeResponseCodeIsSuccessful() { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIsSuccessful', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the response code 2xx - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIsSuccessful() - */ - public function seeResponseCodeIsSuccessful() { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeResponseCodeIsSuccessful', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the response code 3xx - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIsRedirection() - */ - public function canSeeResponseCodeIsRedirection() { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIsRedirection', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the response code 3xx - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIsRedirection() - */ - public function seeResponseCodeIsRedirection() { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeResponseCodeIsRedirection', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the response code is 4xx - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIsClientError() - */ - public function canSeeResponseCodeIsClientError() { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIsClientError', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the response code is 4xx - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIsClientError() - */ - public function seeResponseCodeIsClientError() { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeResponseCodeIsClientError', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the response code is 5xx - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIsServerError() - */ - public function canSeeResponseCodeIsServerError() { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIsServerError', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the response code is 5xx - * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIsServerError() - */ - public function seeResponseCodeIsServerError() { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeResponseCodeIsServerError', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page title contains the given string. - * - * ``` php - * seeInTitle('Blog - Post #1'); - * ?> - * ``` - * - * @param $title - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::seeInTitle() - */ - public function canSeeInTitle($title) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInTitle', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page title contains the given string. - * - * ``` php - * seeInTitle('Blog - Post #1'); - * ?> - * ``` - * - * @param $title - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::seeInTitle() - */ - public function seeInTitle($title) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInTitle', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page title does not contain the given string. - * - * @param $title - * - * @return mixed - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Lib\InnerBrowser::dontSeeInTitle() - */ - public function cantSeeInTitle($title) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInTitle', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that the page title does not contain the given string. - * - * @param $title - * - * @return mixed - * @see \Codeception\Lib\InnerBrowser::dontSeeInTitle() - */ - public function dontSeeInTitle($title) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInTitle', func_get_args())); - } - - - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Switch to iframe or frame on the page. - * - * Example: - * ``` html - *