diff --git a/composer.json b/composer.json index b3d4689..3e50541 100644 --- a/composer.json +++ b/composer.json @@ -3,10 +3,20 @@ { "type": "vcs", "url": "https://github.com/wpcodefactory/wpfactory-promoting-notice" + }, + { + "type": "vcs", + "url": "https://github.com/wpcodefactory/wpfactory-cross-selling" + }, + { + "type": "vcs", + "url": "https://github.com/wpcodefactory/wpfactory-admin-menu" } ], "require": { - "wpfactory/wpfactory-promoting-notice": "*" + "wpfactory/wpfactory-promoting-notice": "*", + "wpfactory/wpfactory-cross-selling": "*", + "wpfactory/wpfactory-admin-menu": "*" }, "require-dev": { "squizlabs/php_codesniffer": "*", @@ -16,6 +26,9 @@ "phpcs": "phpcs --standard=phpcs.xml" }, "config": { - "preferred-install": "dist" + "preferred-install": "dist", + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } } -} \ No newline at end of file +} diff --git a/includes/settings/class-alg-wc-oma-settings-general.php b/includes/settings/class-alg-wc-oma-settings-general.php index c5a7897..045a0ba 100644 --- a/includes/settings/class-alg-wc-oma-settings-general.php +++ b/includes/settings/class-alg-wc-oma-settings-general.php @@ -139,15 +139,15 @@ function get_settings() { 'type' => 'multiselect', 'class' => 'wc-enhanced-select', 'options' => array( - 'is_woocommerce' => __( 'Is WooCommerce', 'popup-notices-for-woocommerce' ), - 'is_shop' => __( 'Is Shop', 'popup-notices-for-woocommerce' ), - 'is_product_category' => __( 'Is Product Category', 'popup-notices-for-woocommerce' ), - 'is_product_tag' => __( 'Is Product Tag', 'popup-notices-for-woocommerce' ), - 'is_product' => __( 'Is Product', 'popup-notices-for-woocommerce' ), - 'is_cart' => __( 'Is Cart', 'popup-notices-for-woocommerce' ), - 'is_checkout' => __( 'Is Checkout', 'popup-notices-for-woocommerce' ), - 'is_account_page' => __( 'Is Account Page', 'popup-notices-for-woocommerce' ), - 'is_wc_endpoint_url' => __( 'Is WC Endpoint URL', 'popup-notices-for-woocommerce' ) + 'is_woocommerce' => __( 'Is WooCommerce', 'order-minimum-amount-for-woocommerce' ), + 'is_shop' => __( 'Is Shop', 'order-minimum-amount-for-woocommerce' ), + 'is_product_category' => __( 'Is Product Category', 'order-minimum-amount-for-woocommerce' ), + 'is_product_tag' => __( 'Is Product Tag', 'order-minimum-amount-for-woocommerce' ), + 'is_product' => __( 'Is Product', 'order-minimum-amount-for-woocommerce' ), + 'is_cart' => __( 'Is Cart', 'order-minimum-amount-for-woocommerce' ), + 'is_checkout' => __( 'Is Checkout', 'order-minimum-amount-for-woocommerce' ), + 'is_account_page' => __( 'Is Account Page', 'order-minimum-amount-for-woocommerce' ), + 'is_wc_endpoint_url' => __( 'Is WC Endpoint URL', 'order-minimum-amount-for-woocommerce' ) ), 'custom_attributes' => apply_filters( 'alg_wc_oma_settings', array( 'disabled' => 'disabled' ) ), ), diff --git a/langs/order-minimum-amount-for-woocommerce-ru_RU.po b/langs/order-minimum-amount-for-woocommerce-ru_RU.po index 9bf4a18..7e18fa5 100644 --- a/langs/order-minimum-amount-for-woocommerce-ru_RU.po +++ b/langs/order-minimum-amount-for-woocommerce-ru_RU.po @@ -691,7 +691,7 @@ msgstr "Общие > Параметры оформления заказа" msgid "General options" msgstr "Общие параметры" -#: order-minimum-amount-for-woocommerce-pro.php:200 +#: order-minimum-amount-for-woocommerce-pro.php:291 msgid "Go Pro" msgstr "Стать профи" @@ -1045,6 +1045,7 @@ msgstr "Опциональные суммы для каждого пользов #: includes/settings/class-alg-wc-oma-settings-general.php:44 #: includes/settings/class-alg-wc-oma-settings-general.php:49 #: includes/settings/class-alg-wc-settings-oma.php:29 +#: order-minimum-amount-for-woocommerce-pro.php:214 msgid "Order Min/Max Amount" msgstr "Минимальная/Максимальная сумма заказа" @@ -1889,3 +1890,43 @@ msgstr "" #: includes/settings/class-alg-wc-oma-settings-user-roles.php:60 msgid "All roles" msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:142 +msgid "Is WooCommerce" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:143 +msgid "Is Shop" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:144 +msgid "Is Product Category" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:145 +msgid "Is Product Tag" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:146 +msgid "Is Product" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:147 +msgid "Is Cart" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:148 +msgid "Is Checkout" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:149 +msgid "Is Account Page" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:150 +msgid "Is WC Endpoint URL" +msgstr "" + +#: order-minimum-amount-for-woocommerce-pro.php:213 +msgid "Order Min/Max" +msgstr "" diff --git a/langs/order-minimum-amount-for-woocommerce.pot b/langs/order-minimum-amount-for-woocommerce.pot index c8167ef..99b4c7e 100644 --- a/langs/order-minimum-amount-for-woocommerce.pot +++ b/langs/order-minimum-amount-for-woocommerce.pot @@ -2,14 +2,14 @@ # This file is distributed under the GNU General Public License v3.0. msgid "" msgstr "" -"Project-Id-Version: order-minimum-amount-for-woocommerce 4.5.2\n" +"Project-Id-Version: order-minimum-amount-for-woocommerce 4.5.3\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/order-minimum-amount-for-woocommerce\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-08-29T21:36:06+02:00\n" +"POT-Creation-Date: 2024-10-19T04:12:09+02:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.7.1\n" "X-Domain: order-minimum-amount-for-woocommerce\n" @@ -519,6 +519,7 @@ msgstr "" #: includes/settings/class-alg-wc-oma-settings-general.php:44 #: includes/settings/class-alg-wc-oma-settings-general.php:49 #: includes/settings/class-alg-wc-settings-oma.php:29 +#: order-minimum-amount-for-woocommerce-pro.php:214 msgid "Order Min/Max Amount" msgstr "" @@ -1076,6 +1077,42 @@ msgstr "" msgid "Display condition" msgstr "" +#: includes/settings/class-alg-wc-oma-settings-general.php:142 +msgid "Is WooCommerce" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:143 +msgid "Is Shop" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:144 +msgid "Is Product Category" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:145 +msgid "Is Product Tag" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:146 +msgid "Is Product" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:147 +msgid "Is Cart" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:148 +msgid "Is Checkout" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:149 +msgid "Is Account Page" +msgstr "" + +#: includes/settings/class-alg-wc-oma-settings-general.php:150 +msgid "Is WC Endpoint URL" +msgstr "" + #: includes/settings/class-alg-wc-oma-settings-general.php:162 msgid "Cart options" msgstr "" @@ -1885,6 +1922,10 @@ msgstr "" msgid "Your settings have been reset." msgstr "" -#: order-minimum-amount-for-woocommerce-pro.php:200 +#: order-minimum-amount-for-woocommerce-pro.php:213 +msgid "Order Min/Max" +msgstr "" + +#: order-minimum-amount-for-woocommerce-pro.php:291 msgid "Go Pro" msgstr "" diff --git a/order-minimum-amount-for-woocommerce.php b/order-minimum-amount-for-woocommerce.php index dc49bcb..98face5 100644 --- a/order-minimum-amount-for-woocommerce.php +++ b/order-minimum-amount-for-woocommerce.php @@ -3,13 +3,13 @@ Plugin Name: Order Minimum/Maximum Amount for WooCommerce Plugin URI: https://wpfactory.com/item/order-minimum-maximum-amount-for-woocommerce/ Description: Set required minimum and/or maximum order amounts (e.g. sum, quantity, weight, volume, etc.) in WooCommerce. -Version: 4.5.2 +Version: 4.5.3 Author: WPFactory Author URI: https://wpfactory.com Text Domain: order-minimum-amount-for-woocommerce Domain Path: /langs Copyright: © 2024 WPFactory -WC tested up to: 9.1 +WC tested up to: 9.3 Requires Plugins: woocommerce License: GNU General Public License v3.0 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -42,6 +42,12 @@ function alg_wc_oma_is_plugin_active( $plugin ) { ! alg_wc_oma_is_plugin_active( 'woocommerce/woocommerce.php' ) || ( 'order-minimum-amount-for-woocommerce.php' === basename( __FILE__ ) && alg_wc_oma_is_plugin_active( 'order-minimum-amount-for-woocommerce-pro/order-minimum-amount-for-woocommerce-pro.php' ) ) ) { + if ( function_exists( 'alg_wc_oma' ) ) { + $plugin = alg_wc_oma(); + if ( method_exists( $plugin, 'set_free_version_filesystem_path' ) ) { + $plugin->set_free_version_filesystem_path( __FILE__ ); + } + } return; } @@ -67,7 +73,7 @@ final class Alg_WC_OMA { * @since 1.0.0 * @var string */ - public $version = '4.5.2'; + public $version = '4.5.3'; /** * $_instance. @@ -95,6 +101,20 @@ final class Alg_WC_OMA { */ public $core; + /** + * $file_system_path. + * + * @since 4.5.3 + */ + protected $file_system_path; + + /** + * $free_version_file_system_path. + * + * @since 4.5.3 + */ + protected $free_version_file_system_path; + /** * Main Alg_WC_OMA Instance. * @@ -116,32 +136,103 @@ public static function instance() { /** * Initializer. * - * @version 4.2.7 + * @version 4.5.3 * @since 4.2.7 * * @access public */ function init() { - // Set up localisation + // Adds cross-selling library. + $this->add_cross_selling_library(); + + // Move WC Settings tab to WPFactory menu. + $this->move_wc_settings_tab_to_wpfactory_menu(); + + // Set up localisation. add_action( 'init', array( $this, 'localize' ) ); + // Adds compatibility with HPOS. + add_action( 'before_woocommerce_init', function () { + $this->declare_compatibility_with_hpos( $this->get_filesystem_path() ); + if ( ! empty( $this->get_free_version_filesystem_path() ) ) { + $this->declare_compatibility_with_hpos( $this->get_free_version_filesystem_path() ); + } + } ); + // Handling dynamic properties warning. require_once 'includes/class-alg-wc-oma-dynamic-properties-obj.php'; - // Pro + // Pro. if ( 'order-minimum-amount-for-woocommerce-pro.php' === basename( __FILE__ ) ) { $this->pro = require_once 'includes/pro/class-alg-wc-oma-pro.php'; } - // Include required files + // Include required files. $this->includes(); - // Admin + // Admin. if ( is_admin() ) { $this->admin(); } } + /** + * add_cross_selling_library. + * + * @version 4.5.3 + * @since 4.5.3 + * + * @return void + */ + function add_cross_selling_library(){ + if ( ! is_admin() ) { + return; + } + // Cross-selling library. + $cross_selling = new \WPFactory\WPFactory_Cross_Selling\WPFactory_Cross_Selling(); + $cross_selling->setup( array( 'plugin_file_path' => $this->get_filesystem_path() ) ); + $cross_selling->init(); + } + + /** + * move_wc_settings_tab_to_wpfactory_submenu. + * + * @version 4.5.3 + * @since 4.5.3 + * + * @return void + */ + function move_wc_settings_tab_to_wpfactory_menu() { + if ( ! is_admin() ) { + return; + } + // WC Settings tab as WPFactory submenu item. + $wpf_admin_menu = \WPFactory\WPFactory_Admin_Menu\WPFactory_Admin_Menu::get_instance(); + $wpf_admin_menu->move_wc_settings_tab_to_wpfactory_menu( array( + 'wc_settings_tab_id' => 'alg_wc_oma', + 'menu_title' => __( 'Order Min/Max', 'order-minimum-amount-for-woocommerce' ), + 'page_title' => __( 'Order Min/Max Amount', 'order-minimum-amount-for-woocommerce' ), + ) ); + } + + /** + * Declare compatibility with custom order tables for WooCommerce. + * + * @version 4.5.3 + * @since 4.5.3 + * + * @param $filesystem_path + * + * @return void + * @link https://github.com/woocommerce/woocommerce/wiki/High-Performance-Order-Storage-Upgrade-Recipe-Book#declaring-extension-incompatibility + * + */ + function declare_compatibility_with_hpos( $filesystem_path ) { + if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) { + \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', $filesystem_path, true ); + } + } + /** * localize. * @@ -248,6 +339,54 @@ function plugin_path() { return untrailingslashit( plugin_dir_path( __FILE__ ) ); } + /** + * get_filesystem_path. + * + * @version 4.5.3 + * @since 4.5.3 + * + * @return string + */ + function get_filesystem_path() { + return $this->file_system_path; + } + + /** + * set_filesystem_path. + * + * @version 4.5.3 + * @since 4.5.3 + * + * @param mixed $file_system_path + */ + public function set_filesystem_path( $file_system_path ) { + $this->file_system_path = $file_system_path; + } + + /** + * get_free_version_filesystem_path. + * + * @version 4.5.3 + * @since 4.5.3 + * + * @return mixed + */ + public function get_free_version_filesystem_path() { + return $this->free_version_file_system_path; + } + + /** + * set_free_version_filesystem_path. + * + * @version 4.5.3 + * @since 4.5.3 + * + * @param mixed $free_version_file_system_path + */ + public function set_free_version_filesystem_path( $free_version_file_system_path ) { + $this->free_version_file_system_path = $free_version_file_system_path; + } + } endif; @@ -266,17 +405,8 @@ function alg_wc_oma() { } } -add_action( - 'plugins_loaded', - function () { - $plugin = alg_wc_oma(); - $plugin->init(); - } -); - - -add_action( 'before_woocommerce_init', function() { - if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) { - \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true ); - } -} ); +add_action( 'plugins_loaded', function () { + $plugin = alg_wc_oma(); + $plugin->set_filesystem_path( __FILE__ ); + $plugin->init(); +} ); \ No newline at end of file diff --git a/readme.txt b/readme.txt index d6d202c..c7d0b57 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: wpcodefactory, omardabbas, karzin, anbinder, algoritmika, kousikmu Tags: woocommerce, order minimum amount, order maximum amount Requires at least: 6.1 Tested up to: 6.6 -Stable tag: 4.5.2 +Stable tag: 4.5.3 License: GNU General Public License v3.0 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -360,6 +360,13 @@ Once activated, access the plugin's settings by navigating to “WooCommerce > S == Changelog == += 4.5.3 - 18/10/2024 = +* Dev - Add Cross-selling library. +* Dev - Move settings to WPFactory menu. +* Dev - Added the License Key Manager library. +* Dev - Improve compatibility with HPOS. +* WC tested up to: 9.3. + = 4.5.2 - 29/08/2024 = * Fix - Fees class cleared. * Fix - Fixed possible javascript error on cart page related to wcBlocksData. diff --git a/vendor/autoload.php b/vendor/autoload.php index 5efb664..091b031 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -2,6 +2,24 @@ // autoload.php @generated by Composer +if (PHP_VERSION_ID < 50600) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL; + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, $err); + } elseif (!headers_sent()) { + echo $err; + } + } + trigger_error( + $err, + E_USER_ERROR + ); +} + require_once __DIR__ . '/composer/autoload_real.php'; return ComposerAutoloaderInit57e04eae08304ab82d3ce6ceec9390f1::getLoader(); diff --git a/vendor/bin/phpcbf b/vendor/bin/phpcbf index e192088..0b62200 100644 --- a/vendor/bin/phpcbf +++ b/vendor/bin/phpcbf @@ -4,21 +4,117 @@ /** * Proxy PHP file generated by Composer * - * This file includes the referenced bin path (../squizlabs/php_codesniffer/bin/phpcbf) using eval to remove the shebang if present + * This file includes the referenced bin path (../squizlabs/php_codesniffer/bin/phpcbf) + * using a stream wrapper to prevent the shebang from being output on PHP<8 * * @generated */ -$binPath = realpath(__DIR__ . "/" . '../squizlabs/php_codesniffer/bin/phpcbf'); -$contents = file_get_contents($binPath); -$contents = preg_replace('{^#!/.+\r?\n<\?(php)?}', '', $contents, 1, $replaced); -if ($replaced) { - $contents = strtr($contents, array( - '__FILE__' => var_export($binPath, true), - '__DIR__' => var_export(dirname($binPath), true), - )); - - eval($contents); - exit(0); +namespace Composer; + +$GLOBALS['_composer_bin_dir'] = __DIR__; +$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php'; + +if (PHP_VERSION_ID < 80000) { + if (!class_exists('Composer\BinProxyWrapper')) { + /** + * @internal + */ + final class BinProxyWrapper + { + private $handle; + private $position; + private $realpath; + + public function stream_open($path, $mode, $options, &$opened_path) + { + // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution + $opened_path = substr($path, 17); + $this->realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + include("phpvfscomposer://" . __DIR__ . '/..'.'/squizlabs/php_codesniffer/bin/phpcbf'); + exit(0); + } } -include $binPath; + +include __DIR__ . '/..'.'/squizlabs/php_codesniffer/bin/phpcbf'; diff --git a/vendor/bin/phpcbf.bat b/vendor/bin/phpcbf.bat index fe902d5..f489428 100644 --- a/vendor/bin/phpcbf.bat +++ b/vendor/bin/phpcbf.bat @@ -1,4 +1,5 @@ @ECHO OFF setlocal DISABLEDELAYEDEXPANSION -SET BIN_TARGET=%~dp0/../squizlabs/php_codesniffer/bin/phpcbf +SET BIN_TARGET=%~dp0/phpcbf +SET COMPOSER_RUNTIME_BIN_DIR=%~dp0 php "%BIN_TARGET%" %* diff --git a/vendor/bin/phpcs b/vendor/bin/phpcs index c1138a2..9eb8455 100644 --- a/vendor/bin/phpcs +++ b/vendor/bin/phpcs @@ -4,21 +4,117 @@ /** * Proxy PHP file generated by Composer * - * This file includes the referenced bin path (../squizlabs/php_codesniffer/bin/phpcs) using eval to remove the shebang if present + * This file includes the referenced bin path (../squizlabs/php_codesniffer/bin/phpcs) + * using a stream wrapper to prevent the shebang from being output on PHP<8 * * @generated */ -$binPath = realpath(__DIR__ . "/" . '../squizlabs/php_codesniffer/bin/phpcs'); -$contents = file_get_contents($binPath); -$contents = preg_replace('{^#!/.+\r?\n<\?(php)?}', '', $contents, 1, $replaced); -if ($replaced) { - $contents = strtr($contents, array( - '__FILE__' => var_export($binPath, true), - '__DIR__' => var_export(dirname($binPath), true), - )); - - eval($contents); - exit(0); +namespace Composer; + +$GLOBALS['_composer_bin_dir'] = __DIR__; +$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php'; + +if (PHP_VERSION_ID < 80000) { + if (!class_exists('Composer\BinProxyWrapper')) { + /** + * @internal + */ + final class BinProxyWrapper + { + private $handle; + private $position; + private $realpath; + + public function stream_open($path, $mode, $options, &$opened_path) + { + // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution + $opened_path = substr($path, 17); + $this->realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + include("phpvfscomposer://" . __DIR__ . '/..'.'/squizlabs/php_codesniffer/bin/phpcs'); + exit(0); + } } -include $binPath; + +include __DIR__ . '/..'.'/squizlabs/php_codesniffer/bin/phpcs'; diff --git a/vendor/bin/phpcs.bat b/vendor/bin/phpcs.bat index 11aab45..ea984e9 100644 --- a/vendor/bin/phpcs.bat +++ b/vendor/bin/phpcs.bat @@ -1,4 +1,5 @@ @ECHO OFF setlocal DISABLEDELAYEDEXPANSION -SET BIN_TARGET=%~dp0/../squizlabs/php_codesniffer/bin/phpcs +SET BIN_TARGET=%~dp0/phpcs +SET COMPOSER_RUNTIME_BIN_DIR=%~dp0 php "%BIN_TARGET%" %* diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php index 6d0c3f2..a72151c 100644 --- a/vendor/composer/ClassLoader.php +++ b/vendor/composer/ClassLoader.php @@ -42,30 +42,79 @@ */ class ClassLoader { + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var ?string */ private $vendorDir; // PSR-4 + /** + * @var array[] + * @psalm-var array> + */ private $prefixLengthsPsr4 = array(); + /** + * @var array[] + * @psalm-var array> + */ private $prefixDirsPsr4 = array(); + /** + * @var array[] + * @psalm-var array + */ private $fallbackDirsPsr4 = array(); // PSR-0 + /** + * @var array[] + * @psalm-var array> + */ private $prefixesPsr0 = array(); + /** + * @var array[] + * @psalm-var array + */ private $fallbackDirsPsr0 = array(); + /** @var bool */ private $useIncludePath = false; + + /** + * @var string[] + * @psalm-var array + */ private $classMap = array(); + + /** @var bool */ private $classMapAuthoritative = false; + + /** + * @var bool[] + * @psalm-var array + */ private $missingClasses = array(); + + /** @var ?string */ private $apcuPrefix; + /** + * @var self[] + */ private static $registeredLoaders = array(); + /** + * @param ?string $vendorDir + */ public function __construct($vendorDir = null) { $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); } + /** + * @return string[] + */ public function getPrefixes() { if (!empty($this->prefixesPsr0)) { @@ -75,28 +124,47 @@ public function getPrefixes() return array(); } + /** + * @return array[] + * @psalm-return array> + */ public function getPrefixesPsr4() { return $this->prefixDirsPsr4; } + /** + * @return array[] + * @psalm-return array + */ public function getFallbackDirs() { return $this->fallbackDirsPsr0; } + /** + * @return array[] + * @psalm-return array + */ public function getFallbackDirsPsr4() { return $this->fallbackDirsPsr4; } + /** + * @return string[] Array of classname => path + * @psalm-return array + */ public function getClassMap() { return $this->classMap; } /** - * @param array $classMap Class to filename map + * @param string[] $classMap Class to filename map + * @psalm-param array $classMap + * + * @return void */ public function addClassMap(array $classMap) { @@ -111,9 +179,11 @@ public function addClassMap(array $classMap) * Registers a set of PSR-0 directories for a given prefix, either * appending or prepending to the ones previously set for this prefix. * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void */ public function add($prefix, $paths, $prepend = false) { @@ -156,11 +226,13 @@ public function add($prefix, $paths, $prepend = false) * Registers a set of PSR-4 directories for a given namespace, either * appending or prepending to the ones previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException + * + * @return void */ public function addPsr4($prefix, $paths, $prepend = false) { @@ -204,8 +276,10 @@ public function addPsr4($prefix, $paths, $prepend = false) * Registers a set of PSR-0 directories for a given prefix, * replacing any others previously set for this prefix. * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 base directories + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 base directories + * + * @return void */ public function set($prefix, $paths) { @@ -220,10 +294,12 @@ public function set($prefix, $paths) * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories * * @throws \InvalidArgumentException + * + * @return void */ public function setPsr4($prefix, $paths) { @@ -243,6 +319,8 @@ public function setPsr4($prefix, $paths) * Turns on searching the include path for class files. * * @param bool $useIncludePath + * + * @return void */ public function setUseIncludePath($useIncludePath) { @@ -265,6 +343,8 @@ public function getUseIncludePath() * that have not been registered with the class map. * * @param bool $classMapAuthoritative + * + * @return void */ public function setClassMapAuthoritative($classMapAuthoritative) { @@ -285,6 +365,8 @@ public function isClassMapAuthoritative() * APCu prefix to use to cache found/not-found classes, if the extension is enabled. * * @param string|null $apcuPrefix + * + * @return void */ public function setApcuPrefix($apcuPrefix) { @@ -305,6 +387,8 @@ public function getApcuPrefix() * Registers this instance as an autoloader. * * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void */ public function register($prepend = false) { @@ -324,6 +408,8 @@ public function register($prepend = false) /** * Unregisters this instance as an autoloader. + * + * @return void */ public function unregister() { @@ -343,7 +429,8 @@ public function unregister() public function loadClass($class) { if ($file = $this->findFile($class)) { - includeFile($file); + $includeFile = self::$includeFile; + $includeFile($file); return true; } @@ -403,6 +490,11 @@ public static function getRegisteredLoaders() return self::$registeredLoaders; } + /** + * @param string $class + * @param string $ext + * @return string|false + */ private function findFileWithExtension($class, $ext) { // PSR-4 lookup @@ -468,14 +560,26 @@ private function findFileWithExtension($class, $ext) return false; } -} -/** - * Scope isolated include. - * - * Prevents access to $this/self from included files. - */ -function includeFile($file) -{ - include $file; + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } } diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php index b3a4e16..51e734a 100644 --- a/vendor/composer/InstalledVersions.php +++ b/vendor/composer/InstalledVersions.php @@ -20,12 +20,27 @@ * * See also https://getcomposer.org/doc/07-runtime.md#installed-versions * - * To require it's presence, you can require `composer-runtime-api ^2.0` + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final */ class InstalledVersions { + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ private static $installed; + + /** + * @var bool|null + */ private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ private static $installedByVendor = array(); /** @@ -83,7 +98,7 @@ public static function isInstalled($packageName, $includeDevRequirements = true) { foreach (self::getInstalled() as $installed) { if (isset($installed['versions'][$packageName])) { - return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']); + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; } } @@ -104,7 +119,7 @@ public static function isInstalled($packageName, $includeDevRequirements = true) */ public static function satisfies(VersionParser $parser, $packageName, $constraint) { - $constraint = $parser->parseConstraints($constraint); + $constraint = $parser->parseConstraints((string) $constraint); $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); return $provided->matches($constraint); @@ -228,7 +243,7 @@ public static function getInstallPath($packageName) /** * @return array - * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string} + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} */ public static function getRootPackage() { @@ -242,7 +257,7 @@ public static function getRootPackage() * * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. * @return array[] - * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array} + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} */ public static function getRawData() { @@ -265,7 +280,7 @@ public static function getRawData() * Returns the raw data of all installed.php which are currently loaded for custom implementations * * @return array[] - * @psalm-return list}> + * @psalm-return list}> */ public static function getAllRawData() { @@ -288,7 +303,7 @@ public static function getAllRawData() * @param array[] $data A vendor/composer/installed.php data set * @return void * - * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array} $data + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data */ public static function reload($data) { @@ -298,7 +313,7 @@ public static function reload($data) /** * @return array[] - * @psalm-return list}> + * @psalm-return list}> */ private static function getInstalled() { @@ -313,7 +328,9 @@ private static function getInstalled() if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; } elseif (is_file($vendorDir.'/composer/installed.php')) { - $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + $installed[] = self::$installedByVendor[$vendorDir] = $required; if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { self::$installed = $installed[count($installed) - 1]; } @@ -325,12 +342,17 @@ private static function getInstalled() // only require the installed.php file if this file is loaded from its dumped location, // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 if (substr(__DIR__, -8, 1) !== 'C') { - self::$installed = require __DIR__ . '/installed.php'; + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; } else { self::$installed = array(); } } - $installed[] = self::$installed; + + if (self::$installed !== array()) { + $installed[] = self::$installed; + } return $installed; } diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index dee7469..c8d4d1b 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -2,10 +2,54 @@ // autoload_classmap.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', + 'PHPCSUtils\\AbstractSniffs\\AbstractArrayDeclarationSniff' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/AbstractSniffs/AbstractArrayDeclarationSniff.php', + 'PHPCSUtils\\BackCompat\\BCFile' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCFile.php', + 'PHPCSUtils\\BackCompat\\BCTokens' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCTokens.php', + 'PHPCSUtils\\BackCompat\\Helper' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/Helper.php', + 'PHPCSUtils\\Exceptions\\InvalidTokenArray' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/InvalidTokenArray.php', + 'PHPCSUtils\\Exceptions\\TestFileNotFound' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestFileNotFound.php', + 'PHPCSUtils\\Exceptions\\TestMarkerNotFound' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestMarkerNotFound.php', + 'PHPCSUtils\\Exceptions\\TestTargetNotFound' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestTargetNotFound.php', + 'PHPCSUtils\\Fixers\\SpacesFixer' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Fixers/SpacesFixer.php', + 'PHPCSUtils\\Internal\\Cache' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/Cache.php', + 'PHPCSUtils\\Internal\\IsShortArrayOrList' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/IsShortArrayOrList.php', + 'PHPCSUtils\\Internal\\IsShortArrayOrListWithCache' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/IsShortArrayOrListWithCache.php', + 'PHPCSUtils\\Internal\\NoFileCache' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/NoFileCache.php', + 'PHPCSUtils\\Internal\\StableCollections' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/StableCollections.php', + 'PHPCSUtils\\TestUtils\\UtilityMethodTestCase' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/TestUtils/UtilityMethodTestCase.php', + 'PHPCSUtils\\Tokens\\Collections' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Tokens/Collections.php', + 'PHPCSUtils\\Tokens\\TokenHelper' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Tokens/TokenHelper.php', + 'PHPCSUtils\\Utils\\Arrays' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Arrays.php', + 'PHPCSUtils\\Utils\\Conditions' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Conditions.php', + 'PHPCSUtils\\Utils\\Context' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Context.php', + 'PHPCSUtils\\Utils\\ControlStructures' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ControlStructures.php', + 'PHPCSUtils\\Utils\\FunctionDeclarations' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/FunctionDeclarations.php', + 'PHPCSUtils\\Utils\\GetTokensAsString' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/GetTokensAsString.php', + 'PHPCSUtils\\Utils\\Lists' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Lists.php', + 'PHPCSUtils\\Utils\\MessageHelper' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/MessageHelper.php', + 'PHPCSUtils\\Utils\\Namespaces' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Namespaces.php', + 'PHPCSUtils\\Utils\\NamingConventions' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/NamingConventions.php', + 'PHPCSUtils\\Utils\\Numbers' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Numbers.php', + 'PHPCSUtils\\Utils\\ObjectDeclarations' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ObjectDeclarations.php', + 'PHPCSUtils\\Utils\\Operators' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Operators.php', + 'PHPCSUtils\\Utils\\Orthography' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Orthography.php', + 'PHPCSUtils\\Utils\\Parentheses' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Parentheses.php', + 'PHPCSUtils\\Utils\\PassedParameters' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/PassedParameters.php', + 'PHPCSUtils\\Utils\\Scopes' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Scopes.php', + 'PHPCSUtils\\Utils\\TextStrings' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/TextStrings.php', + 'PHPCSUtils\\Utils\\UseStatements' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/UseStatements.php', + 'PHPCSUtils\\Utils\\Variables' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Variables.php', 'WPFactory\\Promoting_Notice\\Core' => $vendorDir . '/wpfactory/wpfactory-promoting-notice/src/php/class-core.php', + 'WPFactory\\WPFactory_Admin_Menu\\Singleton' => $vendorDir . '/wpfactory/wpfactory-admin-menu/src/php/trait-singleton.php', + 'WPFactory\\WPFactory_Admin_Menu\\WC_Settings_Menu_Item_Swapper' => $vendorDir . '/wpfactory/wpfactory-admin-menu/src/php/class-wc-settings-menu-item-swapper.php', + 'WPFactory\\WPFactory_Admin_Menu\\WPFactory_Admin_Menu' => $vendorDir . '/wpfactory/wpfactory-admin-menu/src/php/class-wpfactory-admin-menu.php', + 'WPFactory\\WPFactory_Cross_Selling\\Product_Categories' => $vendorDir . '/wpfactory/wpfactory-cross-selling/src/php/class-product-categories.php', + 'WPFactory\\WPFactory_Cross_Selling\\Products' => $vendorDir . '/wpfactory/wpfactory-cross-selling/src/php/class-products.php', + 'WPFactory\\WPFactory_Cross_Selling\\Singleton' => $vendorDir . '/wpfactory/wpfactory-cross-selling/src/php/trait-singleton.php', + 'WPFactory\\WPFactory_Cross_Selling\\WPFactory_Cross_Selling' => $vendorDir . '/wpfactory/wpfactory-cross-selling/src/php/class-wpfactory-cross-selling.php', ); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 491dd66..3bea4a8 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -2,7 +2,7 @@ // autoload_files.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php index b7fc012..15a2ff3 100644 --- a/vendor/composer/autoload_namespaces.php +++ b/vendor/composer/autoload_namespaces.php @@ -2,7 +2,7 @@ // autoload_namespaces.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index b265c64..e5bf926 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -2,8 +2,9 @@ // autoload_psr4.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( + 'PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => array($vendorDir . '/dealerdirect/phpcodesniffer-composer-installer/src'), ); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 07877b6..f04be3c 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -23,51 +23,26 @@ public static function getLoader() } spl_autoload_register(array('ComposerAutoloaderInit57e04eae08304ab82d3ce6ceec9390f1', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); spl_autoload_unregister(array('ComposerAutoloaderInit57e04eae08304ab82d3ce6ceec9390f1', 'loadClassLoader')); - $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); - if ($useStaticLoader) { - require __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; + call_user_func(\Composer\Autoload\ComposerStaticInit57e04eae08304ab82d3ce6ceec9390f1::getInitializer($loader)); - call_user_func(\Composer\Autoload\ComposerStaticInit57e04eae08304ab82d3ce6ceec9390f1::getInitializer($loader)); - } else { - $map = require __DIR__ . '/autoload_namespaces.php'; - foreach ($map as $namespace => $path) { - $loader->set($namespace, $path); - } + $loader->register(true); - $map = require __DIR__ . '/autoload_psr4.php'; - foreach ($map as $namespace => $path) { - $loader->setPsr4($namespace, $path); - } + $filesToLoad = \Composer\Autoload\ComposerStaticInit57e04eae08304ab82d3ce6ceec9390f1::$files; + $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); + require $file; } - } - - $loader->register(true); - - if ($useStaticLoader) { - $includeFiles = Composer\Autoload\ComposerStaticInit57e04eae08304ab82d3ce6ceec9390f1::$files; - } else { - $includeFiles = require __DIR__ . '/autoload_files.php'; - } - foreach ($includeFiles as $fileIdentifier => $file) { - composerRequire57e04eae08304ab82d3ce6ceec9390f1($fileIdentifier, $file); + }, null, null); + foreach ($filesToLoad as $fileIdentifier => $file) { + $requireFile($fileIdentifier, $file); } return $loader; } } - -function composerRequire57e04eae08304ab82d3ce6ceec9390f1($fileIdentifier, $file) -{ - if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { - require $file; - - $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; - } -} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 3273517..958014d 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -10,14 +10,74 @@ class ComposerStaticInit57e04eae08304ab82d3ce6ceec9390f1 '20872bbaff0e3115cc7db5ab4a7d607e' => __DIR__ . '/..' . '/wpfactory/wpfactory-promoting-notice/src/php/functions.php', ); + public static $prefixLengthsPsr4 = array ( + 'P' => + array ( + 'PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => 57, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => + array ( + 0 => __DIR__ . '/..' . '/dealerdirect/phpcodesniffer-composer-installer/src', + ), + ); + public static $classMap = array ( 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'PHPCSUtils\\AbstractSniffs\\AbstractArrayDeclarationSniff' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/AbstractSniffs/AbstractArrayDeclarationSniff.php', + 'PHPCSUtils\\BackCompat\\BCFile' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCFile.php', + 'PHPCSUtils\\BackCompat\\BCTokens' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCTokens.php', + 'PHPCSUtils\\BackCompat\\Helper' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/Helper.php', + 'PHPCSUtils\\Exceptions\\InvalidTokenArray' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/InvalidTokenArray.php', + 'PHPCSUtils\\Exceptions\\TestFileNotFound' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestFileNotFound.php', + 'PHPCSUtils\\Exceptions\\TestMarkerNotFound' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestMarkerNotFound.php', + 'PHPCSUtils\\Exceptions\\TestTargetNotFound' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestTargetNotFound.php', + 'PHPCSUtils\\Fixers\\SpacesFixer' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Fixers/SpacesFixer.php', + 'PHPCSUtils\\Internal\\Cache' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/Cache.php', + 'PHPCSUtils\\Internal\\IsShortArrayOrList' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/IsShortArrayOrList.php', + 'PHPCSUtils\\Internal\\IsShortArrayOrListWithCache' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/IsShortArrayOrListWithCache.php', + 'PHPCSUtils\\Internal\\NoFileCache' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/NoFileCache.php', + 'PHPCSUtils\\Internal\\StableCollections' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/StableCollections.php', + 'PHPCSUtils\\TestUtils\\UtilityMethodTestCase' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/TestUtils/UtilityMethodTestCase.php', + 'PHPCSUtils\\Tokens\\Collections' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Tokens/Collections.php', + 'PHPCSUtils\\Tokens\\TokenHelper' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Tokens/TokenHelper.php', + 'PHPCSUtils\\Utils\\Arrays' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Arrays.php', + 'PHPCSUtils\\Utils\\Conditions' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Conditions.php', + 'PHPCSUtils\\Utils\\Context' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Context.php', + 'PHPCSUtils\\Utils\\ControlStructures' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ControlStructures.php', + 'PHPCSUtils\\Utils\\FunctionDeclarations' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/FunctionDeclarations.php', + 'PHPCSUtils\\Utils\\GetTokensAsString' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/GetTokensAsString.php', + 'PHPCSUtils\\Utils\\Lists' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Lists.php', + 'PHPCSUtils\\Utils\\MessageHelper' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/MessageHelper.php', + 'PHPCSUtils\\Utils\\Namespaces' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Namespaces.php', + 'PHPCSUtils\\Utils\\NamingConventions' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/NamingConventions.php', + 'PHPCSUtils\\Utils\\Numbers' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Numbers.php', + 'PHPCSUtils\\Utils\\ObjectDeclarations' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ObjectDeclarations.php', + 'PHPCSUtils\\Utils\\Operators' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Operators.php', + 'PHPCSUtils\\Utils\\Orthography' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Orthography.php', + 'PHPCSUtils\\Utils\\Parentheses' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Parentheses.php', + 'PHPCSUtils\\Utils\\PassedParameters' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/PassedParameters.php', + 'PHPCSUtils\\Utils\\Scopes' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Scopes.php', + 'PHPCSUtils\\Utils\\TextStrings' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/TextStrings.php', + 'PHPCSUtils\\Utils\\UseStatements' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/UseStatements.php', + 'PHPCSUtils\\Utils\\Variables' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Variables.php', 'WPFactory\\Promoting_Notice\\Core' => __DIR__ . '/..' . '/wpfactory/wpfactory-promoting-notice/src/php/class-core.php', + 'WPFactory\\WPFactory_Admin_Menu\\Singleton' => __DIR__ . '/..' . '/wpfactory/wpfactory-admin-menu/src/php/trait-singleton.php', + 'WPFactory\\WPFactory_Admin_Menu\\WC_Settings_Menu_Item_Swapper' => __DIR__ . '/..' . '/wpfactory/wpfactory-admin-menu/src/php/class-wc-settings-menu-item-swapper.php', + 'WPFactory\\WPFactory_Admin_Menu\\WPFactory_Admin_Menu' => __DIR__ . '/..' . '/wpfactory/wpfactory-admin-menu/src/php/class-wpfactory-admin-menu.php', + 'WPFactory\\WPFactory_Cross_Selling\\Product_Categories' => __DIR__ . '/..' . '/wpfactory/wpfactory-cross-selling/src/php/class-product-categories.php', + 'WPFactory\\WPFactory_Cross_Selling\\Products' => __DIR__ . '/..' . '/wpfactory/wpfactory-cross-selling/src/php/class-products.php', + 'WPFactory\\WPFactory_Cross_Selling\\Singleton' => __DIR__ . '/..' . '/wpfactory/wpfactory-cross-selling/src/php/trait-singleton.php', + 'WPFactory\\WPFactory_Cross_Selling\\WPFactory_Cross_Selling' => __DIR__ . '/..' . '/wpfactory/wpfactory-cross-selling/src/php/class-wpfactory-cross-selling.php', ); public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit57e04eae08304ab82d3ce6ceec9390f1::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit57e04eae08304ab82d3ce6ceec9390f1::$prefixDirsPsr4; $loader->classMap = ComposerStaticInit57e04eae08304ab82d3ce6ceec9390f1::$classMap; }, null, ClassLoader::class); diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 15329ba..361e474 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1,18 +1,271 @@ { "packages": [ + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "4be43904336affa5c2f70744a348312336afd0da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", + "reference": "4be43904336affa5c2f70744a348312336afd0da", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "ext-json": "*", + "ext-zip": "*", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.0", + "yoast/phpunit-polyfills": "^1.0" + }, + "time": "2023-01-05T11:28:13+00:00", + "type": "composer-plugin", + "extra": { + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcbf", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "source": "https://github.com/PHPCSStandards/composer-installer" + }, + "install-path": "../dealerdirect/phpcodesniffer-composer-installer" + }, + { + "name": "phpcsstandards/phpcsextra", + "version": "1.2.1", + "version_normalized": "1.2.1.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", + "reference": "11d387c6642b6e4acaf0bd9bf5203b8cca1ec489" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/11d387c6642b6e4acaf0bd9bf5203b8cca1ec489", + "reference": "11d387c6642b6e4acaf0bd9bf5203b8cca1ec489", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "phpcsstandards/phpcsutils": "^1.0.9", + "squizlabs/php_codesniffer": "^3.8.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcsstandards/phpcsdevcs": "^1.1.6", + "phpcsstandards/phpcsdevtools": "^1.2.1", + "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "time": "2023-12-08T16:49:07+00:00", + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "installation-source": "dist", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors" + } + ], + "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues", + "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSExtra" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "install-path": "../phpcsstandards/phpcsextra" + }, + { + "name": "phpcsstandards/phpcsutils", + "version": "1.0.12", + "version_normalized": "1.0.12.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", + "reference": "87b233b00daf83fb70f40c9a28692be017ea7c6c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/87b233b00daf83fb70f40c9a28692be017ea7c6c", + "reference": "87b233b00daf83fb70f40c9a28692be017ea7c6c", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.10.0 || 4.0.x-dev@dev" + }, + "require-dev": { + "ext-filter": "*", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcsstandards/phpcsdevcs": "^1.1.6", + "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0" + }, + "time": "2024-05-20T13:34:27+00:00", + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "PHPCSUtils/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" + } + ], + "description": "A suite of utility functions for use with PHP_CodeSniffer", + "homepage": "https://phpcsutils.com/", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "phpcs3", + "standards", + "static analysis", + "tokens", + "utility" + ], + "support": { + "docs": "https://phpcsutils.com/", + "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", + "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSUtils" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "install-path": "../phpcsstandards/phpcsutils" + }, { "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "version_normalized": "3.7.2.0", + "version": "3.10.3", + "version_normalized": "3.10.3.0", "source": { "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "62d32998e820bddc40f99f8251958aed187a5c9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/62d32998e820bddc40f99f8251958aed187a5c9c", + "reference": "62d32998e820bddc40f99f8251958aed187a5c9c", "shasum": "" }, "require": { @@ -22,12 +275,12 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" }, - "time": "2023-02-22T23:07:41+00:00", + "time": "2024-09-18T10:38:58+00:00", "bin": [ - "bin/phpcs", - "bin/phpcbf" + "bin/phpcbf", + "bin/phpcs" ], "type": "library", "extra": { @@ -43,52 +296,83 @@ "authors": [ { "name": "Greg Sherwood", - "role": "lead" + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" } ], "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", "keywords": [ "phpcs", "standards", "static analysis" ], "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], "install-path": "../squizlabs/php_codesniffer" }, { "name": "wp-coding-standards/wpcs", - "version": "2.3.0", - "version_normalized": "2.3.0.0", + "version": "3.1.0", + "version_normalized": "3.1.0.0", "source": { "type": "git", "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", - "reference": "7da1894633f168fe244afc6de00d141f27517b62" + "reference": "9333efcbff231f10dfd9c56bb7b65818b4733ca7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7da1894633f168fe244afc6de00d141f27517b62", - "reference": "7da1894633f168fe244afc6de00d141f27517b62", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/9333efcbff231f10dfd9c56bb7b65818b4733ca7", + "reference": "9333efcbff231f10dfd9c56bb7b65818b4733ca7", "shasum": "" }, "require": { + "ext-filter": "*", + "ext-libxml": "*", + "ext-tokenizer": "*", + "ext-xmlreader": "*", "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.3.1" + "phpcsstandards/phpcsextra": "^1.2.1", + "phpcsstandards/phpcsutils": "^1.0.10", + "squizlabs/php_codesniffer": "^3.9.0" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", "phpcompatibility/php-compatibility": "^9.0", - "phpcsstandards/phpcsdevtools": "^1.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + "phpcsstandards/phpcsdevtools": "^1.2.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" }, "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically." + "ext-iconv": "For improved results", + "ext-mbstring": "For improved results" }, - "time": "2020-05-13T23:57:56+00:00", + "time": "2024-03-25T16:39:00+00:00", "type": "phpcodesniffer-standard", "installation-source": "dist", "notification-url": "https://packagist.org/downloads/", @@ -105,6 +389,7 @@ "keywords": [ "phpcs", "standards", + "static analysis", "wordpress" ], "support": { @@ -112,8 +397,119 @@ "source": "https://github.com/WordPress/WordPress-Coding-Standards", "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" }, + "funding": [ + { + "url": "https://opencollective.com/php_codesniffer", + "type": "custom" + } + ], "install-path": "../wp-coding-standards/wpcs" }, + { + "name": "wpfactory/wpfactory-admin-menu", + "version": "v1.0.3", + "version_normalized": "1.0.3.0", + "source": { + "type": "git", + "url": "https://github.com/wpcodefactory/wpfactory-admin-menu.git", + "reference": "fe48c4100a6b436a3dd39308814887119063c140" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wpcodefactory/wpfactory-admin-menu/zipball/fe48c4100a6b436a3dd39308814887119063c140", + "reference": "fe48c4100a6b436a3dd39308814887119063c140", + "shasum": "" + }, + "require-dev": { + "wp-cli/wp-cli-bundle": "*" + }, + "time": "2024-10-14T14:01:02+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/php/" + ] + }, + "scripts": { + "wp": [ + "vendor/bin/wp" + ], + "wp-create-pot": [ + "wp i18n make-pot src/php ./langs/wpfactory-admin-menu.pot --domain=wpfactory-admin-menu" + ], + "wp-update-po": [ + "wp i18n update-po ./langs/wpfactory-admin-menu.pot" + ], + "wp-make-mo": [ + "wp i18n make-mo ./langs/" + ], + "translate": [ + "@composer run wp-create-pot", + "@composer run wp-update-po", + "@composer run wp-make-mo" + ] + }, + "support": { + "source": "https://github.com/wpcodefactory/wpfactory-admin-menu/tree/v1.0.3", + "issues": "https://github.com/wpcodefactory/wpfactory-admin-menu/issues" + }, + "install-path": "../wpfactory/wpfactory-admin-menu" + }, + { + "name": "wpfactory/wpfactory-cross-selling", + "version": "v1.0.2", + "version_normalized": "1.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/wpcodefactory/wpfactory-cross-selling.git", + "reference": "6637ce11b91ee75922c6e7a63c8c5f69429c1990" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wpcodefactory/wpfactory-cross-selling/zipball/6637ce11b91ee75922c6e7a63c8c5f69429c1990", + "reference": "6637ce11b91ee75922c6e7a63c8c5f69429c1990", + "shasum": "" + }, + "require": { + "wpfactory/wpfactory-admin-menu": "*" + }, + "require-dev": { + "wp-cli/wp-cli-bundle": "*" + }, + "time": "2024-10-14T15:28:57+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/php/" + ] + }, + "scripts": { + "wp": [ + "vendor/bin/wp" + ], + "wp-create-pot": [ + "wp i18n make-pot src/php ./langs/wpfactory-cross-selling.pot --domain=wpfactory-cross-selling" + ], + "wp-update-po": [ + "wp i18n update-po ./langs/wpfactory-cross-selling.pot" + ], + "wp-make-mo": [ + "wp i18n make-mo ./langs/" + ], + "translate": [ + "@composer run wp-create-pot", + "@composer run wp-update-po", + "@composer run wp-make-mo" + ] + }, + "support": { + "source": "https://github.com/wpcodefactory/wpfactory-cross-selling/tree/v1.0.2", + "issues": "https://github.com/wpcodefactory/wpfactory-cross-selling/issues" + }, + "install-path": "../wpfactory/wpfactory-cross-selling" + }, { "name": "wpfactory/wpfactory-promoting-notice", "version": "v1.0.6", @@ -149,6 +545,9 @@ ], "dev": true, "dev-package-names": [ + "dealerdirect/phpcodesniffer-composer-installer", + "phpcsstandards/phpcsextra", + "phpcsstandards/phpcsutils", "squizlabs/php_codesniffer", "wp-coding-standards/wpcs" ] diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 5188620..3d24076 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -1,49 +1,94 @@ array( + 'name' => '__root__', 'pretty_version' => 'dev-master', 'version' => 'dev-master', + 'reference' => '87b57417550b7d6551654512e4eba833b6d53a98', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '8124e9c96c76a44987164626994164f94808c13a', - 'name' => '__root__', 'dev' => true, ), 'versions' => array( '__root__' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', + 'reference' => '87b57417550b7d6551654512e4eba833b6d53a98', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '8124e9c96c76a44987164626994164f94808c13a', 'dev_requirement' => false, ), + 'dealerdirect/phpcodesniffer-composer-installer' => array( + 'pretty_version' => 'v1.0.0', + 'version' => '1.0.0.0', + 'reference' => '4be43904336affa5c2f70744a348312336afd0da', + 'type' => 'composer-plugin', + 'install_path' => __DIR__ . '/../dealerdirect/phpcodesniffer-composer-installer', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'phpcsstandards/phpcsextra' => array( + 'pretty_version' => '1.2.1', + 'version' => '1.2.1.0', + 'reference' => '11d387c6642b6e4acaf0bd9bf5203b8cca1ec489', + 'type' => 'phpcodesniffer-standard', + 'install_path' => __DIR__ . '/../phpcsstandards/phpcsextra', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'phpcsstandards/phpcsutils' => array( + 'pretty_version' => '1.0.12', + 'version' => '1.0.12.0', + 'reference' => '87b233b00daf83fb70f40c9a28692be017ea7c6c', + 'type' => 'phpcodesniffer-standard', + 'install_path' => __DIR__ . '/../phpcsstandards/phpcsutils', + 'aliases' => array(), + 'dev_requirement' => true, + ), 'squizlabs/php_codesniffer' => array( - 'pretty_version' => '3.7.2', - 'version' => '3.7.2.0', + 'pretty_version' => '3.10.3', + 'version' => '3.10.3.0', + 'reference' => '62d32998e820bddc40f99f8251958aed187a5c9c', 'type' => 'library', 'install_path' => __DIR__ . '/../squizlabs/php_codesniffer', 'aliases' => array(), - 'reference' => 'ed8e00df0a83aa96acf703f8c2979ff33341f879', 'dev_requirement' => true, ), 'wp-coding-standards/wpcs' => array( - 'pretty_version' => '2.3.0', - 'version' => '2.3.0.0', + 'pretty_version' => '3.1.0', + 'version' => '3.1.0.0', + 'reference' => '9333efcbff231f10dfd9c56bb7b65818b4733ca7', 'type' => 'phpcodesniffer-standard', 'install_path' => __DIR__ . '/../wp-coding-standards/wpcs', 'aliases' => array(), - 'reference' => '7da1894633f168fe244afc6de00d141f27517b62', 'dev_requirement' => true, ), + 'wpfactory/wpfactory-admin-menu' => array( + 'pretty_version' => 'v1.0.3', + 'version' => '1.0.3.0', + 'reference' => 'fe48c4100a6b436a3dd39308814887119063c140', + 'type' => 'library', + 'install_path' => __DIR__ . '/../wpfactory/wpfactory-admin-menu', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'wpfactory/wpfactory-cross-selling' => array( + 'pretty_version' => 'v1.0.2', + 'version' => '1.0.2.0', + 'reference' => '6637ce11b91ee75922c6e7a63c8c5f69429c1990', + 'type' => 'library', + 'install_path' => __DIR__ . '/../wpfactory/wpfactory-cross-selling', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'wpfactory/wpfactory-promoting-notice' => array( 'pretty_version' => 'v1.0.6', 'version' => '1.0.6.0', + 'reference' => '9b0dc91c32662ce9040b36a4b40827454e806856', 'type' => 'library', 'install_path' => __DIR__ . '/../wpfactory/wpfactory-promoting-notice', 'aliases' => array(), - 'reference' => '9b0dc91c32662ce9040b36a4b40827454e806856', 'dev_requirement' => false, ), ), diff --git a/vendor/phpcsstandards/phpcsextra/CHANGELOG.md b/vendor/phpcsstandards/phpcsextra/CHANGELOG.md new file mode 100644 index 0000000..3ee3258 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/CHANGELOG.md @@ -0,0 +1,590 @@ +# Change Log for the PHPCSExtra standard for PHP CodeSniffer + +All notable changes to this project will be documented in this file. + +This projects adheres to [Keep a CHANGELOG](http://keepachangelog.com/) and uses [Semantic Versioning](http://semver.org/). + +**Legend**: +:wrench: = Includes auto-fixer. +:bar_chart: = Includes metrics. +:books: = Includes CLI documentation. + + +## [Unreleased] + +_Nothing yet._ + +## [1.2.1] - 2023-12-08 + +### Changed + +#### Other + +* Composer: The minimum `PHP_CodeSniffer` requirement has been updated to `^3.8.0` (was `^3.7.2`). [#298] +* Composer: The minimum `PHPCSUtils` requirement has been updated to `^1.0.9` (was `^1.0.8`). [#298] + +Please ensure you run `composer update phpcsstandards/phpcsextra --with-dependencies` to benefit from this. + +[#298]: https://github.com/PHPCSStandards/PHPCSExtra/pull/298 + + +## [1.2.0] - 2023-12-02 + +### Added + +#### Universal + +* :wrench: :books: New `Universal.CodeAnalysis.NoDoubleNegative` sniff to detect double negatives (!!) and advise to use a boolean cast instead. Thanks [@diedexx] for reviewing. [#277] +* :wrench: :books: New `Universal.Operators.ConcatPosition` sniff to enforce that the concatenation operator for multi-line concatenations is in a preferred position, either always at the start of the next line or always at the end of the previous line. [#294] +* :wrench: :bar_chart: :books: New `Universal.PHP.LowercasePHPTag` sniff to enforce that the "PHP" in a PHP open tag is lowercase. Thanks [@fredden] for reviewing. [#276] + +### Changed + +#### NormalizedArrays + +* `NormalizedArrays.Arrays.CommaAfterLast`: the sniff now has two extra error codes to distinguish between multi-line arrays with the last array item on the _same line_ as the array closer vs the last array item being on a line _before_ the array closer. Thanks [@stronk7] for suggesting and patching this. [#283], [#284] + These new error codes allow for selectively excluding that specific situation from triggering the sniff. + The new error codes are `FoundMultiLineCloserSameLine` (for `multiLine="forbid"`) and `MissingMultiLineCloserSameLine` (for `multiLine="enforce"`). + The pre-existing `FoundMultiLine` and `FoundSingleLine` error codes continue to be used for multi-line arrays with the last array item on a different line than the array closer. + +#### Other + +* Various housekeeping. + +[#276]: https://github.com/PHPCSStandards/PHPCSExtra/pull/276 +[#277]: https://github.com/PHPCSStandards/PHPCSExtra/pull/277 +[#283]: https://github.com/PHPCSStandards/PHPCSExtra/issues/283 +[#284]: https://github.com/PHPCSStandards/PHPCSExtra/pull/284 +[#294]: https://github.com/PHPCSStandards/PHPCSExtra/pull/294 + + +## [1.1.2] - 2023-09-21 + +### Changed + +#### Other + +* Various housekeeping. + +### Fixed + +#### Universal + +* `Universal.CodeAnalysis.ConstructorDestructorReturn`: the sniff will now correctly ignore methods mirroring the class name (PHP-4 style constructors) in namespaced code. [#207], [#272] + +[#272]: https://github.com/PHPCSStandards/PHPCSExtra/pull/272 + + +## [1.1.1] - 2023-08-26 + +### Changed + +#### Modernize + +* `Modernize.FunctionCalls.Dirname`: the sniff will now respect a potentially set [`php_version` configuration option][php_version-config] and only report on modernizations which are possible on the configured `php_version`. [#261] + If the `php_version` is not set, the sniff will continue to report on all modernization options. + +#### Other + +* Various documentation improvements. Props in part to [@szepeviktor]. +* Improved defensive coding in select places. +* Various housekeeping. + +[#261]: https://github.com/PHPCSStandards/PHPCSExtra/pull/261 + + +## [1.1.0] - 2023-07-19 + +### Added + +#### Universal + +* :wrench: :books: New `Universal.CodeAnalysis.NoEchoSprintf` sniff to detect use of the inefficient `echo [v]sprintf(...);` combi and recommends using `[v]printf()` instead. [#242] +* :bar_chart: :books: New `Universal.FunctionDeclarations.NoLongClosures` sniff to detect "long" closures and recommend using a named function instead. [#240] + The sniff offers the following properties to influence its behaviour: `recommendedLines` (defaults to `5`), `maxLines` (defaults to `8`), `ignoreCommentLines` (defaults to `true`) and `ignoreEmptyLines` (defaults to `true`). +* :wrench: :bar_chart: :books: New `Universal.FunctionDeclarations.RequireFinalMethodsInTraits` sniff to enforce non-private, non-abstract methods in traits to be declared as `final`. [#243], [#245] + There is a separate `NonFinalMagicMethodFound` error code for magic methods to allow those to be excluded from the check. +* :wrench: :bar_chart: :books: New `Universal.UseStatements.DisallowMixedGroupUse` sniff to disallow group use statements which import a combination of namespace/OO construct, functions and/or constants in one statement. [#241], [#246] + Note: the fixer will use a semi-standardized format for group use statements. If there are more specific requirements for the formatting of group use statements, the ruleset configurator should ensure that additional sniffs are included in the ruleset to enforce the required format. +* :wrench: :bar_chart: :books: New `Universal.UseStatements.KeywordSpacing` sniff to enforce the use of a single space after the `use`, `function`, `const` keywords and both before and after the `as` keyword in import `use` statements. [#247] + The sniff has modular error codes to allow for disabling individual checks. +* :wrench: :books: New `Universal.UseStatements.NoUselessAliases` sniff to detect useless aliases (aliasing something to its original name) in import use statements. [#244] + Note: as OO and function names in PHP are case-insensitive, aliasing to the same name, using a different case is also considered useless. +* :wrench: :bar_chart: :books: New `Universal.WhiteSpace.CommaSpacing` sniff to enforce that there is no space before a comma and exactly one space, or a new line, after a comma. [#254] + Additionally, the sniff also enforces that the comma should follow the code and not be placed after a trailing comment. + The sniff has modular error codes to allow for disabling individual checks and checks in certain contexts. + The sniff will respect a potentially set [`php_version` configuration option][php_version-config] when deciding how to handle the spacing after a heredoc/nowdoc closer. + +### Changed + +#### Universal + +* Minor performance improvements for the `Universal.Arrays.DuplicateArrayKey` and the `Universal.CodeAnalysis.ConstructorDestructorReturn` sniffs. [#251], [#252] + +#### Other + +* Composer: The minimum `PHPCSUtils` requirement has been updated to `^1.0.8` (was `^1.0.6`). [#249], [#254] +* Various housekeeping. + +[#240]: https://github.com/PHPCSStandards/PHPCSExtra/pull/240 +[#241]: https://github.com/PHPCSStandards/PHPCSExtra/pull/241 +[#242]: https://github.com/PHPCSStandards/PHPCSExtra/pull/242 +[#243]: https://github.com/PHPCSStandards/PHPCSExtra/pull/243 +[#244]: https://github.com/PHPCSStandards/PHPCSExtra/pull/244 +[#245]: https://github.com/PHPCSStandards/PHPCSExtra/pull/245 +[#246]: https://github.com/PHPCSStandards/PHPCSExtra/pull/246 +[#247]: https://github.com/PHPCSStandards/PHPCSExtra/pull/247 +[#249]: https://github.com/PHPCSStandards/PHPCSExtra/pull/249 +[#251]: https://github.com/PHPCSStandards/PHPCSExtra/pull/251 +[#252]: https://github.com/PHPCSStandards/PHPCSExtra/pull/252 +[#254]: https://github.com/PHPCSStandards/PHPCSExtra/pull/254 + + +## [1.0.4] - 2023-06-18 + +### Changed + +#### Other + +* Composer: The minimum `PHPCSUtils` requirement has been updated to `^1.0.6` (was `^1.0.0`). [#237] +* Various housekeeping. + +### Fixed + +#### Universal + +* `Universal.Constants.LowercaseClassResolutionKeyword`: prevent false positives for function calls to methods called `class`. [#226] + +[#226]: https://github.com/PHPCSStandards/PHPCSExtra/pull/226 +[#237]: https://github.com/PHPCSStandards/PHPCSExtra/pull/237 + + +## [1.0.3] - 2023-03-28 + +### Changed + +#### Universal + +* `Universal.WhiteSpace.DisallowInlineTabs`: significant performance improvement. [#216], [#217] + +#### Other + +* Various housekeeping. + +### Fixed + +#### Modernize + +* `Modernize.FunctionCalls.Dirname`: prevent false positives for attribute classes called `dirname`. [#211], [#213] + +[#211]: https://github.com/PHPCSStandards/PHPCSExtra/pull/211 +[#213]: https://github.com/PHPCSStandards/PHPCSExtra/pull/213 +[#216]: https://github.com/PHPCSStandards/PHPCSExtra/pull/216 +[#217]: https://github.com/PHPCSStandards/PHPCSExtra/pull/217 + + +## [1.0.2] - 2023-01-10 + +### Changed + +#### Universal + +* `Universal.CodeAnalysis.ConstructorDestructorReturn`: the sniff will now respect a potentially set [`php_version` configuration option][php_version-config] and only report on PHP4-style constructors when the `php_version` is below `'80000'`. Thanks [@anomiex] for reporting! [#207], [#208] + +[#207]: https://github.com/PHPCSStandards/PHPCSExtra/issues/207 +[#208]: https://github.com/PHPCSStandards/PHPCSExtra/pull/208 + + +## [1.0.1] - 2023-01-05 + +### Fixed + +#### Universal + +* `Universal.CodeAnalysis.ConstructorDestructorReturn`: fixed false positive for return statements in nested functions/closures declared within constructor/destructor methods. Thanks [@anomiex] for reporting! [#201], [#202] + +[#201]: https://github.com/PHPCSStandards/PHPCSExtra/issues/201 +[#202]: https://github.com/PHPCSStandards/PHPCSExtra/pull/202 + + +## [1.0.0] - 2023-01-04 + +:warning: Important: this package now requires [PHPCSUtils 1.0.0]. Please make sure you use `--with-[all-]dependencies` when running `composer update`. :exclamation: + +For the full list of features, please see the changelogs of the alpha/rc releases: +* [1.0.0-rc1](https://github.com/PHPCSStandards/PHPCSExtra/releases/tag/1.0.0-rc1) +* [1.0.0-alpha3](https://github.com/PHPCSStandards/PHPCSExtra/releases/tag/1.0.0-alpha3) +* [1.0.0-alpha2](https://github.com/PHPCSStandards/PHPCSExtra/releases/tag/1.0.0-alpha2) +* [1.0.0-alpha1](https://github.com/PHPCSStandards/PHPCSExtra/releases/tag/1.0.0-alpha1) + +### Changed + +#### Other + +* Updated various sniffs to take advantage of PHPCSUtils 1.0.0(-rc1). [#193], [#194], [#195] +* Minor documentation improvements. +* Various housekeeping. + +### Fixed + +#### Modernize + +* `Modernize.FunctionCalls.Dirname`: the sniff will now correctly recognize magic constants in a case-insensitive manner. [#187] + +[PHPCSUtils 1.0.0]: https://github.com/PHPCSStandards/PHPCSUtils/releases/tag/1.0.0 + +[#187]: https://github.com/PHPCSStandards/PHPCSExtra/pull/187 +[#193]: https://github.com/PHPCSStandards/PHPCSExtra/pull/193 +[#194]: https://github.com/PHPCSStandards/PHPCSExtra/pull/194 +[#195]: https://github.com/PHPCSStandards/PHPCSExtra/pull/195 + + +## [1.0.0-RC1] - 2022-12-07 + +:warning: Important: this package now requires [PHPCSUtils 1.0.0-alpha4]. Please make sure you use `--with-[all-]dependencies` when running `composer update`. :exclamation: + +### Added + +#### Modernize + +* This is a new standard with one sniff to start with. +* :wrench: :books: New `Modernize.FunctionCalls.Dirname` sniff to detect and auto-fix two typical code modernizations which can be made related to the [`dirname()`][php-manual-dirname] function. [#172] + +#### Universal + +* :wrench: :bar_chart: :books: New `Universal.Classes.DisallowAnonClassParentheses` sniff to disallow the use of parentheses when declaring an anonymous class without passing parameters. [#76], [#162] +* :wrench: :bar_chart: :books: New `Universal.Classes.RequireAnonClassParentheses` sniff to require the use of parentheses when declaring an anonymous class, whether parameters are passed or not. [#76], [#166] +* :wrench: :bar_chart: :books: New `Universal.Classes.DisallowFinalClass` sniff to disallow classes being declared `final`. [#108], [#114], [#148], [#163] +* :wrench: :bar_chart: :books: New `Universal.Classes.RequireFinalClass` sniff to require all non-`abstract` classes to be declared `final`. [#109], [#148], [#164] + Warning: the auto-fixer for this sniff _may_ have unintended side-effects for applications and should be used with care! This is considered a _risky_ fixer. +* :wrench: :bar_chart: :books: New `Universal.Classes.ModifierKeywordOrder` sniff to standardize the modifier keyword order for class declarations. [#142] + The sniff offers an `order` property to specify the preferred order. +* :wrench: :books: New `Universal.CodeAnalysis.ConstructorDestructorReturn` sniff to verify that class constructor/destructor methods 1) do not have a return type declaration and 2) do not return a value. [#137], [#140], [#146] Inspired by [@derickr]. +* :wrench: :books: New `Universal.CodeAnalysis.ForeachUniqueAssignment` sniff to detect `foreach` control structures which use the same variable for both the key as well as the value assignment as this will lead to unexpected - and most likely unintended - behaviour. [#110], [#175] + The fixer will maintain the existing behaviour of the code. Mind: this may not be the _intended_ behaviour. +* :wrench: :books: New `Universal.CodeAnalysis.StaticInFinalClass` sniff to detect using `static` instead of `self` in OO constructs which are `final`. [#116], [#180] + The sniff has modular error codes to allow for making exceptions based on the type of use for `static`. +* :wrench: :bar_chart: :books: New `Universal.Constants.LowercaseClassResolutionKeyword` sniff to enforce that the `class` keyword when used for class name resolution, i.e. `::class`, is in lowercase. [#72] +* :wrench: :bar_chart: :books: New `Universal.Constants.ModifierKeywordOrder` sniff to standardize the modifier keyword order for OO constant declarations. [#143] + The sniff offers an `order` property to specify the preferred order. +* :wrench: :books: New `Universal.ControlStructures.DisallowLonelyIf` sniff to disallow `if` statements as the only statement in an `else` block. [#85], [#168], [#169] + Inspired by the [ESLint "no lonely if"] rule. + Note: This sniff will not fix the indentation of the "inner" code. It is strongly recommended to run this sniff together with the `Generic.WhiteSpace.ScopeIndent` sniff to get the correct indentation. +* :bar_chart: :books: New `Universal.Files.SeparateFunctionsFromOO` sniff to enforce that a file should either declare (global/namespaced) functions or declare OO structures, but not both. [#95], [#170], [#171] + Nested function declarations, i.e. functions declared within a function/method will be disregarded for the purposes of this sniff. + The same goes for anonymous classes, closures and arrow functions. +* :books: New `Universal.NamingConventions.NoReservedKeywordParameterNames` sniff to verify that function parameters do not use reserved keywords as names, as this can quickly become confusing when people use them in function calls using named parameters. [#80], [#81], [#106], [#107], [#173] + The sniff has modular error codes to allow for making exceptions for specific keywords. +* :wrench: :bar_chart: :books: New `Universal.Operators.TypeSeparatorSpacing` sniff to enforce no spaces around union type and intersection type separators. [#117] +* :wrench: :books: New `Universal.PHP.OneStatementInShortEchoTag` sniff to disallow short open echo tags `= 8.0][php-rfc-negative_array_index]. [#177], [#178] + If a [`php_version` configuration option][php_version-config] has been passed to PHPCS, it will be respected by the sniff and only report duplicate keys for the configured PHP version. +* `Universal.ControlStructures.DisallowAlternativeSyntax`: the sniff will now also record a metric when single-line (no body) control structures are encountered. [#158] +* `Universal.ControlStructures.DisallowAlternativeSyntax`: the error message thrown by the sniff is now more descriptive. [#159] +* `Universal.ControlStructures.DisallowAlternativeSyntax`: metrics will no longer be recorded for `elseif` and `else` keywords, but only on the `if` keyword as the type of syntax used has to be the same for the whole "chain". [#161] +* `Universal.Lists.DisallowLongListSyntax`: the sniff will no longer record (incomplete) metrics about long vs short list usage. [#155] +* `Universal.Lists.DisallowShortListSyntax`: the sniff will now record (complete) metrics about long vs short list usage. [#155] +* `Universal.OOStructures.AlphabeticExtendsImplements`: documented support for `enum ... implements`. [#150] +* `Universal.UseStatements.DisallowUseClass`: updated error message and metric name to take PHP 8.1 `enum`s into account. [#149] +* `Universal.UseStatements.NoLeadingBackslash`: the sniff will now also flag and auto-fix leading backslashes in group use statements. [#167] + +#### Other +* Updated the sniffs for compatibility with PHPCSUtils 1.0.0-alpha4. [#134] +* Updated the sniffs to correctly handle PHP 8.0/8.1/8.2 features whenever relevant. +* Readme: Updated installation instructions for compatibility with Composer 2.2+. [#101] +* Composer: The minimum `PHP_CodeSniffer` requirement has been updated to `^3.7.1` (was `^3.3.1`). [#115], [#130] +* Composer: The package will now identify itself as a static analysis tool. Thanks [@GaryJones]! [#126] +* All non-`abstract` classes in this package are now `final`. [#121] +* All XML documentation now has a schema annotation. [#128] +* Various housekeeping. + +### Fixed + +The upgrade to PHPCSUtils 1.0.0-alpha4 took care of a number of bugs, which potentially could have affected sniffs in this package. + +#### NormalizedArrays +* `NormalizedArrays.Arrays.ArrayBraceSpacing`: the sniff now allows for trailing comments after the array opener in multi-line arrays. [#118] +* `NormalizedArrays.Arrays.ArrayBraceSpacing`: trailing comments at the end of an array, but before the closer, in multi-line arrays will no longer confuse the sniff. [#135] +* `NormalizedArrays.Arrays.CommaAfterLast`: the fixer will now recognize PHP 7.3+ flexible heredoc/nowdocs and in that case, will add the comma on the same line as the heredoc/nowdoc closer. [#144] + +#### Universal +* `Universal.Arrays.DisallowShortArraySyntax`: nested short arrays in short lists will now be detected and fixed correctly. [#153] +* `Universal.ControlStructures.DisallowAlternativeSyntax`: the sniff will no longer bow out indiscriminately when the `allowWithInlineHTML` property is set to `true`. [#90], [#161] +* `Universal.ControlStructures.DisallowAlternativeSyntax`: when alternative control structure syntax is allowed in combination with inline HTML (`allowWithInlineHTML` property set to `true`), inline HTML in functions declared within the control structure body will no longer be taken into account for determining whether or not the control structure contains inline HTML. [#160] +* `Universal.Lists.DisallowShortListSyntax`: the sniff will work around a tokenizer bug in PHPCS 3.7.1, which previously could lead to false negatives. [#151]. +* `Universal.Lists.DisallowShortListSyntax`: nested short lists in short arrays will now be detected and fixed correctly. [#152] +* `Universal.Operators.DisallowStandalonePostIncrementDecrement`: the sniff will now correctly recognize stand-alone statements which end on a PHP close tag. [#176] + +[#72]: https://github.com/PHPCSStandards/PHPCSExtra/pull/72 +[#76]: https://github.com/PHPCSStandards/PHPCSExtra/pull/76 +[#80]: https://github.com/PHPCSStandards/PHPCSExtra/pull/80 +[#81]: https://github.com/PHPCSStandards/PHPCSExtra/pull/81 +[#85]: https://github.com/PHPCSStandards/PHPCSExtra/pull/85 +[#89]: https://github.com/PHPCSStandards/PHPCSExtra/pull/89 +[#90]: https://github.com/PHPCSStandards/PHPCSExtra/pull/90 +[#95]: https://github.com/PHPCSStandards/PHPCSExtra/pull/95 +[#101]: https://github.com/PHPCSStandards/PHPCSExtra/pull/101 +[#106]: https://github.com/PHPCSStandards/PHPCSExtra/pull/106 +[#107]: https://github.com/PHPCSStandards/PHPCSExtra/pull/107 +[#108]: https://github.com/PHPCSStandards/PHPCSExtra/pull/108 +[#109]: https://github.com/PHPCSStandards/PHPCSExtra/pull/109 +[#110]: https://github.com/PHPCSStandards/PHPCSExtra/pull/110 +[#114]: https://github.com/PHPCSStandards/PHPCSExtra/pull/114 +[#115]: https://github.com/PHPCSStandards/PHPCSExtra/pull/115 +[#116]: https://github.com/PHPCSStandards/PHPCSExtra/pull/116 +[#117]: https://github.com/PHPCSStandards/PHPCSExtra/pull/117 +[#118]: https://github.com/PHPCSStandards/PHPCSExtra/pull/118 +[#119]: https://github.com/PHPCSStandards/PHPCSExtra/pull/119 +[#120]: https://github.com/PHPCSStandards/PHPCSExtra/pull/120 +[#121]: https://github.com/PHPCSStandards/PHPCSExtra/pull/121 +[#122]: https://github.com/PHPCSStandards/PHPCSExtra/pull/122 +[#123]: https://github.com/PHPCSStandards/PHPCSExtra/pull/123 +[#124]: https://github.com/PHPCSStandards/PHPCSExtra/pull/124 +[#126]: https://github.com/PHPCSStandards/PHPCSExtra/pull/126 +[#128]: https://github.com/PHPCSStandards/PHPCSExtra/pull/128 +[#130]: https://github.com/PHPCSStandards/PHPCSExtra/pull/130 +[#134]: https://github.com/PHPCSStandards/PHPCSExtra/pull/134 +[#135]: https://github.com/PHPCSStandards/PHPCSExtra/pull/135 +[#137]: https://github.com/PHPCSStandards/PHPCSExtra/pull/137 +[#140]: https://github.com/PHPCSStandards/PHPCSExtra/pull/140 +[#142]: https://github.com/PHPCSStandards/PHPCSExtra/pull/142 +[#143]: https://github.com/PHPCSStandards/PHPCSExtra/pull/143 +[#144]: https://github.com/PHPCSStandards/PHPCSExtra/pull/144 +[#146]: https://github.com/PHPCSStandards/PHPCSExtra/pull/146 +[#147]: https://github.com/PHPCSStandards/PHPCSExtra/pull/147 +[#148]: https://github.com/PHPCSStandards/PHPCSExtra/pull/148 +[#149]: https://github.com/PHPCSStandards/PHPCSExtra/pull/149 +[#150]: https://github.com/PHPCSStandards/PHPCSExtra/pull/150 +[#151]: https://github.com/PHPCSStandards/PHPCSExtra/pull/151 +[#152]: https://github.com/PHPCSStandards/PHPCSExtra/pull/152 +[#153]: https://github.com/PHPCSStandards/PHPCSExtra/pull/153 +[#154]: https://github.com/PHPCSStandards/PHPCSExtra/pull/154 +[#155]: https://github.com/PHPCSStandards/PHPCSExtra/pull/155 +[#158]: https://github.com/PHPCSStandards/PHPCSExtra/pull/158 +[#159]: https://github.com/PHPCSStandards/PHPCSExtra/pull/159 +[#160]: https://github.com/PHPCSStandards/PHPCSExtra/pull/160 +[#161]: https://github.com/PHPCSStandards/PHPCSExtra/pull/161 +[#162]: https://github.com/PHPCSStandards/PHPCSExtra/pull/162 +[#163]: https://github.com/PHPCSStandards/PHPCSExtra/pull/163 +[#164]: https://github.com/PHPCSStandards/PHPCSExtra/pull/164 +[#165]: https://github.com/PHPCSStandards/PHPCSExtra/pull/165 +[#166]: https://github.com/PHPCSStandards/PHPCSExtra/pull/166 +[#167]: https://github.com/PHPCSStandards/PHPCSExtra/pull/167 +[#168]: https://github.com/PHPCSStandards/PHPCSExtra/pull/168 +[#169]: https://github.com/PHPCSStandards/PHPCSExtra/pull/169 +[#170]: https://github.com/PHPCSStandards/PHPCSExtra/pull/170 +[#171]: https://github.com/PHPCSStandards/PHPCSExtra/pull/171 +[#172]: https://github.com/PHPCSStandards/PHPCSExtra/pull/172 +[#173]: https://github.com/PHPCSStandards/PHPCSExtra/pull/173 +[#175]: https://github.com/PHPCSStandards/PHPCSExtra/pull/175 +[#176]: https://github.com/PHPCSStandards/PHPCSExtra/pull/176 +[#177]: https://github.com/PHPCSStandards/PHPCSExtra/pull/177 +[#178]: https://github.com/PHPCSStandards/PHPCSExtra/pull/178 +[#180]: https://github.com/PHPCSStandards/PHPCSExtra/pull/180 + +[php-manual-dirname]: https://www.php.net/function.dirname +[php-rfc-negative_array_index]: https://wiki.php.net/rfc/negative_array_index +[ESLint "no lonely if"]: https://eslint.org/docs/rules/no-lonely-if +[PHPCSUtils 1.0.0-alpha4]: https://github.com/PHPCSStandards/PHPCSUtils/releases/tag/1.0.0-alpha4 + + +## [1.0.0-alpha3] - 2020-06-29 + +### Added + +#### Universal + +* :wrench: :books: New `Universal.Arrays.DisallowShortArraySyntax` sniff to disallow short array syntax. [#40] + In contrast to the PHPCS native `Generic.Arrays.DisallowShortArraySyntax` sniff, this sniff will ignore short list syntax and not cause parse errors when the fixer is used. +* :wrench: :bar_chart: :books: New `Universal.Constants.UppercaseMagicConstants` sniff to enforce that PHP native magic constants are in uppercase. [#64] +* :bar_chart: :books: New `Universal.Namespaces.DisallowDeclarationWithoutName` sniff to disallow namespace declarations without a namespace name. [#50] +* :bar_chart: :books: New `Universal.Operators.DisallowLogicalAndOr` sniff to enforce the use of the boolean `&&` and `||` operators instead of the logical `and`/`or` operators. [#52] + Note: as the [operator precedence] of the logical operators is significantly lower than the operator precedence of boolean operators, this sniff does not contain an auto-fixer. +* :bar_chart: :books: New `Universal.Operators.DisallowShortTernary` sniff to disallow the use of short ternaries `?:`. [#42] + While short ternaries are useful when used correctly, the principle of them is often misunderstood and they are more often than not used incorrectly, leading to hard to debug issues and/or PHP warnings/notices. +* :wrench: :bar_chart: :books: New `Universal.Operators.DisallowStandalonePostIncrementDecrement` sniff disallow the use of post-in/decrements in stand-alone statements and discourage the use of multiple increment/decrement operators in a stand-alone statement. [#65] +* :wrench: :bar_chart: :books: New `Universal.Operators.StrictComparisons` sniff to enforce the use of strict comparisons. [#48] + Warning: the auto-fixer for this sniff _may_ cause bugs in applications and should be used with care! This is considered a _risky_ fixer. +* :wrench: :bar_chart: :books: New `Universal.OOStructures.AlphabeticExtendsImplements` sniff to verify that the names used in a class "implements" statement or an interface "extends" statement are listed in alphabetic order. [#55] + * This sniff contains a public `orderby` property to determine the sort order to use for the statement. + If all names used are unqualified, the sort order won't make a difference. + However, if one or more of the names are partially or fully qualified, the chosen sort order will determine how the sorting between unqualified, partially and fully qualified names is handled. + The sniff supports two sort order options: + - _'name'_ : sort by the interface name only (default); + - _'full'_ : sort by the full name as used in the statement (without leading backslash). + In both cases, the sorting will be done using natural sort, case-insensitive. + * The sniff has modular error codes to allow for selective inclusion/exclusion: + - `ImplementsWrongOrder` - for "class implements" statements. + - `ImplementsWrongOrderWithComments` - for "class implements" statements interlaced with comments. These will not be auto-fixed. + - `ExtendsWrongOrder` - for "interface extends" statements. + - `ExtendsWrongOrderWithComments` - for "interface extends" statements interlaced with comments. These will not be auto-fixed. + * When fixing, the existing spacing between the names in an `implements`/`extends` statement will not be maintained. + The fixer will separate each name with a comma and one space. + If alternative formatting is desired, a sniff which will check and fix the formatting should be added to the ruleset. +* :wrench: :bar_chart: :books: New `Universal.UseStatements.LowercaseFunctionConst` sniff to enforce that `function` and `const` keywords when used in an import `use` statement are always lowercase. [#58] +* :wrench: :bar_chart: :books: New `Universal.UseStatements.NoLeadingBackslash` sniff to verify that a name being imported in an import `use` statement does not start with a leading backslash. [#46] + Names in import `use` statements should always be fully qualified, so a leading backslash is not needed and it is strongly recommended not to use one. + This sniff handles all types of import use statements supported by PHP, in contrast to other sniffs for the same in, for instance, the PSR12 or the Slevomat standard, which are incomplete. +* :wrench: :books: New `Universal.WhiteSpace.DisallowInlineTabs` sniff to enforce using spaces for mid-line alignment. [#43] + +### Changed + +#### Other +* The `master` branch has been renamed to `stable`. +* Composer: The version requirements for the [Composer PHPCS plugin] have been widened to allow for version 0.7.0 which supports Composer 2.0.0. [#62] +* Various housekeeping. + +[#40]: https://github.com/PHPCSStandards/PHPCSExtra/pull/40 +[#42]: https://github.com/PHPCSStandards/PHPCSExtra/pull/42 +[#43]: https://github.com/PHPCSStandards/PHPCSExtra/pull/43 +[#46]: https://github.com/PHPCSStandards/PHPCSExtra/pull/46 +[#48]: https://github.com/PHPCSStandards/PHPCSExtra/pull/48 +[#50]: https://github.com/PHPCSStandards/PHPCSExtra/pull/50 +[#52]: https://github.com/PHPCSStandards/PHPCSExtra/pull/52 +[#55]: https://github.com/PHPCSStandards/PHPCSExtra/pull/55 +[#58]: https://github.com/PHPCSStandards/PHPCSExtra/pull/58 +[#62]: https://github.com/PHPCSStandards/PHPCSExtra/pull/62 +[#64]: https://github.com/PHPCSStandards/PHPCSExtra/pull/64 +[#65]: https://github.com/PHPCSStandards/PHPCSExtra/pull/65 + +[operator precedence]: https://www.php.net/language.operators.precedence + + +## [1.0.0-alpha2] - 2020-02-18 + +### Added + +#### Universal +* :wrench: :bar_chart: :books: New `Universal.ControlStructures.DisallowAlternativeSyntax` sniff to disallow using the alternative syntax for control structures. [#23] + - This sniff contains a `allowWithInlineHTML` property to allow alternative syntax when inline HTML is used within the control structure. In all other cases, the use of the alternative syntax will still be disallowed. + - The sniff has modular error codes to allow for making exceptions based on specific control structures and/or specific control structures in combination with inline HTML. +* :bar_chart: `Universal.UseStatements.DisallowUseClass/Function/Const`: new, additional metrics about the import source will be shown in the `info` report. [#25] + +#### Other +* Readme: installation instructions and sniff list. [#26] + +### Changed + +#### Universal +* `Universal.Arrays.DuplicateArrayKey`: wording of the error message. [#18] +* `Universal.UseStatements.DisallowUseClass/Function/Const`: the error codes have been made more modular. [#25] + Each of these sniffs now has four additional error codes: +
    +
  • FoundSameNamespace, FoundSameNamespaceWithAlias for use statements importing from the same namespace;
  • +
  • FoundGlobalNamespace, FoundGlobalNamespaceWithAlias for use statements importing from the global namespace, like import statements for PHP native classes, functions and constants.
  • +
+ In all other circumstances, the existing error codes FoundWithAlias and FoundWithoutAlias will continue to be used. + +#### Other +* Improved formatting of the CLI documentation which can be viewed using `--generator=text`. [#17] +* Various housekeeping. + +### Fixed + +#### Universal +* `Universal.Arrays.DuplicateArrayKey`: improved handling of parse errors. [#34] +* `Universal.ControlStructures.IfElseDeclaration`: the fixer will now respect tab indentation. [#19] +* `Universal.UseStatements.DisallowUseClass/Function/Const`: the determination of whether a import is aliased in now done in a case-insensitive manner. [#25] +* `Universal.UseStatements.DisallowUseClass/Function/Const`: an import from the global namespace would previously always be seen as non-aliased, even when it was aliased. [#25] +* `Universal.UseStatements.DisallowUseClass/Function/Const`: improved tolerance for `use` import statements with leading backslashes. [#25] + +[#17]: https://github.com/PHPCSStandards/PHPCSExtra/pull/17 +[#18]: https://github.com/PHPCSStandards/PHPCSExtra/pull/18 +[#19]: https://github.com/PHPCSStandards/PHPCSExtra/pull/19 +[#23]: https://github.com/PHPCSStandards/PHPCSExtra/pull/23 +[#25]: https://github.com/PHPCSStandards/PHPCSExtra/pull/25 +[#26]: https://github.com/PHPCSStandards/PHPCSExtra/pull/26 +[#34]: https://github.com/PHPCSStandards/PHPCSExtra/pull/34 + + +## 1.0.0-alpha1 - 2020-01-23 + +Initial alpha release containing: +* A `NormalizedArrays` standard which will contain a full set of sniffs to check the formatting of array declarations. +* A `Universal` standard which will contain a collection of universal sniffs. + DO NOT INCLUDE THIS AS A STANDARD. + `Universal`, like the upstream PHPCS `Generic` standard, contains sniffs which contradict each other. + Include individual sniffs from this standard in a custom project/company ruleset to use them. + +This initial alpha release contains the following sniffs: + +### NormalizedArrays +* :wrench: :bar_chart: :books: `NormalizedArrays.Arrays.ArrayBraceSpacing`: enforce consistent spacing for the open/close braces of array declarations. + The sniff allows for having different settings for: + - Space between the array keyword and the open parenthesis for long arrays via the `keywordSpacing` property. + Accepted values: (int) number of spaces or `false` to turn this check off. Defaults to `0` spaces. + - Spaces on the inside of the braces for empty arrays via the `spacesWhenEmpty` property. + Accepted values: (string) `newline`, (int) number of spaces or `false` to turn this check off. Defaults to `0` spaces. + - Spaces on the inside of the braces for single-line arrays via the `spacesSingleLine` property; + Accepted values: (int) number of spaces or `false` to turn this check off. Defaults to `0` spaces. + - Spaces on the inside of the braces for multi-line arrays via the `spacesMultiLine` property. + Accepted values: (string) `newline`, (int) number of spaces or `false` to turn this check off. Defaults to `newline`. + Note: if any of the above properties are set to `newline`, it is recommended to also include an array indentation sniff. This sniff will not handle the indentation. +* :wrench: :bar_chart: :books: `NormalizedArrays.Arrays.CommaAfterLast`: enforce/forbid a comma after the last item in an array declaration. + By default, this sniff will: +
    +
  • forbid a comma after the last array item for single-line arrays.
  • +
  • enforce a comma after the last array item for multi-line arrays.
  • +
+ This can be changed for each type or array individually by setting the singleLine or multiLine properties in a custom ruleset. + The valid values are: enforce, forbid or skip to not check the comma after the last array item for a particular type of array. + +### Universal +* :books: `Universal.Arrays.DuplicateArrayKey`: detects duplicate array keys in array declarations. +* :books: `Universal.Arrays.MixedArrayKeyTypes`: best practice sniff: don't use a mix of integer and numeric keys for array items. +* :books: `Universal.Arrays.MixedKeyedUnkeyedArray`: best practice sniff: don't use a mix of keyed and unkeyed array items. +* :wrench: :bar_chart: :books: `Universal.ControlStructures.IfElseDeclaration`: verify that else(if) statements with braces are on a new line. +* :wrench: :bar_chart: :books: `Universal.Lists.DisallowLongListSyntax`: disallow the use of long `list`s. +* :wrench: :bar_chart: :books: `Universal.Lists.DisallowShortListSyntax`: disallow the use of short lists. +* :bar_chart: :books: `Universal.Namespaces.DisallowCurlyBraceSyntax`: disallow the use of the alternative namespace declaration syntax using curly braces. +* :bar_chart: :books: `Universal.Namespaces.EnforceCurlyBraceSyntax`: enforce the use of the alternative namespace syntax using curly braces. +* :books: `Universal.Namespaces.OneDeclarationPerFile`: disallow the use of multiple namespaces within a file. +* :bar_chart: :books: `Universal.UseStatements.DisallowUseClass`: forbid using import use statements for classes/traits/interfaces. + Individual sub-types can be allowed by excluding specific error codes. +* :bar_chart: :books: `Universal.UseStatements.DisallowUseConst`: forbid using import use statements for constants. + Individual sub-types can be allowed by excluding specific error codes. +* :bar_chart: :books: `Universal.UseStatements.DisallowUseFunction`: forbid using import use statements for functions. + Individual sub-types can be allowed by excluding specific error codes. + +[Composer PHPCS plugin]: https://github.com/PHPCSStandards/composer-installer +[php_version-config]: https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki/Configuration-Options#setting-the-php-version + +[Unreleased]: https://github.com/PHPCSStandards/PHPCSExtra/compare/stable...HEAD +[1.2.1]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.2.0...1.2.1 +[1.2.0]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.1.2...1.2.0 +[1.1.2]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.1.1...1.1.2 +[1.1.1]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.1.0...1.1.1 +[1.1.0]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.4...1.1.0 +[1.0.4]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.3...1.0.4 +[1.0.3]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.2...1.0.3 +[1.0.2]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.1...1.0.2 +[1.0.1]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.0...1.0.1 +[1.0.0]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.0-rc1...1.0.0 +[1.0.0-RC1]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.0-alpha3...1.0.0-rc1 +[1.0.0-alpha3]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.0-alpha2...1.0.0-alpha3 +[1.0.0-alpha2]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.0-alpha1...1.0.0-alpha2 + +[@anomiex]: https://github.com/anomiex +[@derickr]: https://github.com/derickr +[@diedexx]: https://github.com/diedexx +[@fredden]: https://github.com/fredden +[@GaryJones]: https://github.com/GaryJones +[@stronk7]: https://github.com/stronk7 +[@szepeviktor]: https://github.com/szepeviktor diff --git a/vendor/phpcsstandards/phpcsextra/LICENSE b/vendor/phpcsstandards/phpcsextra/LICENSE new file mode 100644 index 0000000..0a04128 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/vendor/phpcsstandards/phpcsextra/Modernize/Docs/FunctionCalls/DirnameStandard.xml b/vendor/phpcsstandards/phpcsextra/Modernize/Docs/FunctionCalls/DirnameStandard.xml new file mode 100644 index 0000000..5f48664 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Modernize/Docs/FunctionCalls/DirnameStandard.xml @@ -0,0 +1,40 @@ + + + + = 5.3: Usage of dirname(__FILE__) can be replaced with __DIR__. + ]]> + + + + __DIR__; + ]]> + + + dirname(__FILE__); + ]]> + + + + = 7.0: Nested calls to dirname() can be replaced by using dirname() with the $levels parameter. + ]]> + + + + dirname($file, 3); + ]]> + + + dirname(dirname(dirname($file))); + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Modernize/Sniffs/FunctionCalls/DirnameSniff.php b/vendor/phpcsstandards/phpcsextra/Modernize/Sniffs/FunctionCalls/DirnameSniff.php new file mode 100644 index 0000000..b064b26 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Modernize/Sniffs/FunctionCalls/DirnameSniff.php @@ -0,0 +1,382 @@ + + */ + public function register() + { + return [\T_STRING]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if (isset($this->phpVersion) === false || \defined('PHP_CODESNIFFER_IN_TESTS')) { + // Set default value to prevent this code from running every time the sniff is triggered. + $this->phpVersion = 0; + + $phpVersion = Helper::getConfigData('php_version'); + if ($phpVersion !== null) { + $this->phpVersion = (int) $phpVersion; + } + } + + if ($this->phpVersion !== 0 && $this->phpVersion < 50300) { + // PHP version too low, nothing to do. + return; + } + + $tokens = $phpcsFile->getTokens(); + + if (\strtolower($tokens[$stackPtr]['content']) !== 'dirname') { + // Not our target. + return; + } + + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($nextNonEmpty === false + || $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS + || isset($tokens[$nextNonEmpty]['parenthesis_owner']) === true + ) { + // Not our target. + return; + } + + if (isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false) { + // Live coding or parse error, ignore. + return; + } + + if (Context::inAttribute($phpcsFile, $stackPtr) === true) { + // Class instantiation in attribute, not function call. + return; + } + + // Check if it is really a function call to the global function. + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + + if (isset(Collections::objectOperators()[$tokens[$prevNonEmpty]['code']]) === true + || $tokens[$prevNonEmpty]['code'] === \T_NEW + ) { + // Method call, class instantiation or other "not our target". + return; + } + + if ($tokens[$prevNonEmpty]['code'] === \T_NS_SEPARATOR) { + $prevPrevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevNonEmpty - 1), null, true); + if ($tokens[$prevPrevToken]['code'] === \T_STRING + || $tokens[$prevPrevToken]['code'] === \T_NAMESPACE + ) { + // Namespaced function. + return; + } + } + + /* + * As of here, we can be pretty sure this is a function call to the global function. + */ + $opener = $nextNonEmpty; + $closer = $tokens[$nextNonEmpty]['parenthesis_closer']; + + $parameters = PassedParameters::getParameters($phpcsFile, $stackPtr); + $paramCount = \count($parameters); + if (empty($parameters) || $paramCount > 2) { + // No parameters or too many parameter. + return; + } + + $pathParam = PassedParameters::getParameterFromStack($parameters, 1, 'path'); + if ($pathParam === false) { + // If the path parameter doesn't exist, there's nothing to do. + return; + } + + $levelsParam = PassedParameters::getParameterFromStack($parameters, 2, 'levels'); + if ($levelsParam === false && $paramCount === 2) { + // There must be a typo in the param name or an otherwise stray parameter. Ignore. + return; + } + + /* + * PHP 5.3+: Detect use of dirname(__FILE__). + */ + if (\strtoupper($pathParam['clean']) === '__FILE__') { + $levelsValue = false; + + // Determine if the issue is auto-fixable. + $hasComment = $phpcsFile->findNext(Tokens::$commentTokens, ($opener + 1), $closer); + $fixable = ($hasComment === false); + + if ($fixable === true) { + $levelsValue = $this->getLevelsValue($phpcsFile, $levelsParam); + if ($levelsParam !== false && $levelsValue === false) { + // Can't autofix if we don't know the value of the $levels parameter. + $fixable = false; + } + } + + $error = 'Use the __DIR__ constant instead of calling dirname(__FILE__) (PHP >= 5.3)'; + $code = 'FileConstant'; + + // Throw the error. + if ($fixable === false) { + $phpcsFile->addError($error, $stackPtr, $code); + return; + } + + $fix = $phpcsFile->addFixableError($error, $stackPtr, $code); + if ($fix === true) { + if ($levelsParam === false || $levelsValue === 1) { + // No $levels or $levels set to 1: we can replace the complete function call. + $phpcsFile->fixer->beginChangeset(); + + $phpcsFile->fixer->replaceToken($stackPtr, '__DIR__'); + + for ($i = ($stackPtr + 1); $i <= $closer; $i++) { + $phpcsFile->fixer->replaceToken($i, ''); + } + + // Remove potential leading \. + if ($tokens[$prevNonEmpty]['code'] === \T_NS_SEPARATOR) { + $phpcsFile->fixer->replaceToken($prevNonEmpty, ''); + } + + $phpcsFile->fixer->endChangeset(); + } else { + // We can replace the $path parameter and will need to adjust the $levels parameter. + $filePtr = $phpcsFile->findNext(\T_FILE, $pathParam['start'], ($pathParam['end'] + 1)); + $levelsPtr = $phpcsFile->findNext(\T_LNUMBER, $levelsParam['start'], ($levelsParam['end'] + 1)); + + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->replaceToken($filePtr, '__DIR__'); + $phpcsFile->fixer->replaceToken($levelsPtr, ($levelsValue - 1)); + $phpcsFile->fixer->endChangeset(); + } + } + + return; + } + + /* + * PHP 7.0+: Detect use of nested calls to dirname(). + */ + if ($this->phpVersion !== 0 && $this->phpVersion < 70000) { + // No need to check for this issue if the PHP version would not allow for it anyway. + return; + } + + if (\preg_match('`^\s*\\\\?dirname\s*\(`i', $pathParam['clean']) !== 1) { + return; + } + + /* + * Check if there is something _behind_ the nested dirname() call within the same parameter. + * + * Note: the findNext() calls are safe and will always match the dirname() function call + * as otherwise the above regex wouldn't have matched. + */ + $innerDirnamePtr = $phpcsFile->findNext(\T_STRING, $pathParam['start'], ($pathParam['end'] + 1)); + $innerOpener = $phpcsFile->findNext(\T_OPEN_PARENTHESIS, ($innerDirnamePtr + 1), ($pathParam['end'] + 1)); + if (isset($tokens[$innerOpener]['parenthesis_closer']) === false) { + // Shouldn't be possible. + return; // @codeCoverageIgnore + } + + $innerCloser = $tokens[$innerOpener]['parenthesis_closer']; + if ($innerCloser !== $pathParam['end']) { + $hasContentAfter = $phpcsFile->findNext( + Tokens::$emptyTokens, + ($innerCloser + 1), + ($pathParam['end'] + 1), + true + ); + if ($hasContentAfter !== false) { + // Matched code like: `dirname(dirname($file) . 'something')`. Ignore. + return; + } + } + + /* + * Determine if this is an auto-fixable error. + */ + + // Step 1: Are there comments ? If so, not auto-fixable as we don't want to remove comments. + $fixable = true; + $outerLevelsValue = false; + $innerParameters = []; + $innerLevelsParam = false; + $innerLevelsValue = false; + + for ($i = ($opener + 1); $i < $closer; $i++) { + if (isset(Tokens::$commentTokens[$tokens[$i]['code']])) { + $fixable = false; + break; + } + + if ($tokens[$i]['code'] === \T_OPEN_PARENTHESIS + && isset($tokens[$i]['parenthesis_closer']) + ) { + // Skip over everything within the nested dirname() function call. + $i = $tokens[$i]['parenthesis_closer']; + } + } + + // Step 2: Does the `$levels` parameter exist for the outer dirname() call and if so, is it usable ? + if ($fixable === true) { + $outerLevelsValue = $this->getLevelsValue($phpcsFile, $levelsParam); + if ($levelsParam !== false && $outerLevelsValue === false) { + // Can't autofix if we don't know the value of the $levels parameter. + $fixable = false; + } + } + + // Step 3: Does the `$levels` parameter exist for the inner dirname() call and if so, is it usable ? + if ($fixable === true) { + $innerParameters = PassedParameters::getParameters($phpcsFile, $innerDirnamePtr); + $innerLevelsParam = PassedParameters::getParameterFromStack($innerParameters, 2, 'levels'); + $innerLevelsValue = $this->getLevelsValue($phpcsFile, $innerLevelsParam); + if ($innerLevelsParam !== false && $innerLevelsValue === false) { + // Can't autofix if we don't know the value of the $levels parameter. + $fixable = false; + } + } + + /* + * Throw the error. + */ + $error = 'Pass the $levels parameter to the dirname() call instead of using nested dirname() calls'; + $error .= ' (PHP >= 7.0)'; + $code = 'Nested'; + + if ($fixable === false) { + $phpcsFile->addError($error, $stackPtr, $code); + return; + } + + $fix = $phpcsFile->addFixableError($error, $stackPtr, $code); + if ($fix === false) { + return; + } + + /* + * Fix the error. + */ + $phpcsFile->fixer->beginChangeset(); + + // Remove the info in the _outer_ param call. + for ($i = $opener; $i < $innerOpener; $i++) { + $phpcsFile->fixer->replaceToken($i, ''); + } + + for ($i = ($innerCloser + 1); $i <= $closer; $i++) { + $phpcsFile->fixer->replaceToken($i, ''); + } + + if ($innerLevelsParam !== false) { + // Inner $levels parameter already exists, just adjust the value. + $innerLevelsPtr = $phpcsFile->findNext( + \T_LNUMBER, + $innerLevelsParam['start'], + ($innerLevelsParam['end'] + 1) + ); + $phpcsFile->fixer->replaceToken($innerLevelsPtr, ($innerLevelsValue + $outerLevelsValue)); + } else { + // Inner $levels parameter does not exist yet. We need to add it. + $content = ', '; + + $prevBeforeCloser = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($innerCloser - 1), null, true); + if ($tokens[$prevBeforeCloser]['code'] === \T_COMMA) { + // Trailing comma found, no need to add the comma. + $content = ' '; + } + + $innerPathParam = PassedParameters::getParameterFromStack($innerParameters, 1, 'path'); + if (isset($innerPathParam['name_token']) === true) { + // Non-named param cannot follow named param, so add param name. + $content .= 'levels: '; + } + + $content .= ($innerLevelsValue + $outerLevelsValue); + $phpcsFile->fixer->addContentBefore($innerCloser, $content); + } + + $phpcsFile->fixer->endChangeset(); + } + + /** + * Determine the value of the $levels parameter passed to dirname(). + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param array|false $levelsParam The information about the parameter as retrieved + * via PassedParameters::getParameterFromStack(). + * + * @return int|false Integer levels value or FALSE if the levels value couldn't be determined. + */ + private function getLevelsValue($phpcsFile, $levelsParam) + { + if ($levelsParam === false) { + return 1; + } + + $ignore = Tokens::$emptyTokens; + $ignore[] = \T_LNUMBER; + + $hasNonNumber = $phpcsFile->findNext($ignore, $levelsParam['start'], ($levelsParam['end'] + 1), true); + if ($hasNonNumber !== false) { + return false; + } + + return (int) $levelsParam['clean']; + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Modernize/ruleset.xml b/vendor/phpcsstandards/phpcsextra/Modernize/ruleset.xml new file mode 100644 index 0000000..c30d745 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Modernize/ruleset.xml @@ -0,0 +1,5 @@ + + + + A collection of sniffs to detect code modernization opportunities. + diff --git a/vendor/phpcsstandards/phpcsextra/NormalizedArrays/Docs/Arrays/ArrayBraceSpacingStandard.xml b/vendor/phpcsstandards/phpcsextra/NormalizedArrays/Docs/Arrays/ArrayBraceSpacingStandard.xml new file mode 100644 index 0000000..e8a006f --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/NormalizedArrays/Docs/Arrays/ArrayBraceSpacingStandard.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + (1, 2); + ]]> + + + + + + + + + + + ); + +$args = [ ]; + ]]> + + + + + + + + + + + 1, 2 ); + +$args = [ 1, 2 ]; + ]]> + + + + + + + + + 1, + 2 +); + +$args = [ + 1, + 2 +]; + ]]> + + + + + + diff --git a/vendor/phpcsstandards/phpcsextra/NormalizedArrays/Docs/Arrays/CommaAfterLastStandard.xml b/vendor/phpcsstandards/phpcsextra/NormalizedArrays/Docs/Arrays/CommaAfterLastStandard.xml new file mode 100644 index 0000000..c5094c5 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/NormalizedArrays/Docs/Arrays/CommaAfterLastStandard.xml @@ -0,0 +1,43 @@ + + + + no comma after the last array item. + + However, for multi-line arrays, there should be a comma after the last array item. + ]]> + + + + + + + , ); + ]]> + + + + + 'foo', + 2 => 'bar', +]; + ]]> + + + 'foo', + 2 => 'bar' +]; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/NormalizedArrays/Sniffs/Arrays/ArrayBraceSpacingSniff.php b/vendor/phpcsstandards/phpcsextra/NormalizedArrays/Sniffs/Arrays/ArrayBraceSpacingSniff.php new file mode 100644 index 0000000..b631770 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/NormalizedArrays/Sniffs/Arrays/ArrayBraceSpacingSniff.php @@ -0,0 +1,305 @@ + + */ + public function register() + { + return Collections::arrayOpenTokensBC(); + } + + /** + * Processes this test when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the + * token was found. + * @param int $stackPtr The position in the PHP_CodeSniffer + * file's token stack where the token + * was found. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + /* + * Normalize the public settings. + */ + if ($this->keywordSpacing !== false) { + $this->keywordSpacing = \max((int) $this->keywordSpacing, 0); + } + + if ($this->spacesSingleLine !== false) { + $this->spacesSingleLine = \max((int) $this->spacesSingleLine, 0); + } + + if ($this->spacesMultiLine !== false && $this->spacesMultiLine !== 'newline') { + $this->spacesMultiLine = \max((int) $this->spacesMultiLine, 0); + } + + if ($this->spacesWhenEmpty !== false && $this->spacesWhenEmpty !== 'newline') { + $this->spacesWhenEmpty = \max((int) $this->spacesWhenEmpty, 0); + } + + if ($this->keywordSpacing === false + && $this->spacesSingleLine === false + && $this->spacesMultiLine === false + && $this->spacesWhenEmpty === false + ) { + // Nothing to do. Why was the sniff turned on at all ? + return; + } + + $openClose = Arrays::getOpenClose($phpcsFile, $stackPtr); + if ($openClose === false) { + // Live coding, short list or real square brackets. + return; + } + + $tokens = $phpcsFile->getTokens(); + $opener = $openClose['opener']; + $closer = $openClose['closer']; + + /* + * Check the spacing between the array keyword and the open parenthesis for long arrays. + */ + if ($tokens[$stackPtr]['code'] === \T_ARRAY && $this->keywordSpacing !== false) { + $error = 'There should be %s between the "array" keyword and the open parenthesis. Found: %s'; + $code = 'SpaceAfterKeyword'; + + SpacesFixer::checkAndFix( + $phpcsFile, + $stackPtr, + $opener, + $this->keywordSpacing, + $error, + $code, + 'error', + 0, + 'Space between array keyword and open brace' + ); + } + + /* + * Check for empty arrays. + */ + $nextNonWhiteSpace = $phpcsFile->findNext(\T_WHITESPACE, ($opener + 1), null, true); + if ($nextNonWhiteSpace === $closer) { + if ($this->spacesWhenEmpty === false) { + // Check was turned off. + return; + } + + $error = 'There should be %s between the array opener and closer for an empty array. Found: %s'; + $code = 'EmptyArraySpacing'; + + SpacesFixer::checkAndFix( + $phpcsFile, + $opener, + $closer, + $this->spacesWhenEmpty, + $error, + $code, + 'error', + 0, + 'Space between open and close brace for an empty array' + ); + + return; + } + + /* + * Check non-empty arrays. + */ + if ($tokens[$opener]['line'] === $tokens[$closer]['line']) { + // Single line array. + if ($this->spacesSingleLine === false) { + // Check was turned off. + return; + } + + $error = 'Expected %s after the array opener in a single line array. Found: %s'; + $code = 'SpaceAfterArrayOpenerSingleLine'; + + SpacesFixer::checkAndFix( + $phpcsFile, + $opener, + $phpcsFile->findNext(\T_WHITESPACE, ($opener + 1), null, true), + $this->spacesSingleLine, + $error, + $code, + 'error', + 0, + 'Space after array opener, single line array' + ); + + $error = 'Expected %s before the array closer in a single line array. Found: %s'; + $code = 'SpaceBeforeArrayCloserSingleLine'; + + SpacesFixer::checkAndFix( + $phpcsFile, + $closer, + $phpcsFile->findPrevious(\T_WHITESPACE, ($closer - 1), null, true), + $this->spacesSingleLine, + $error, + $code, + 'error', + 0, + 'Space before array closer, single line array' + ); + + return; + } + + // Multi-line array. + if ($this->spacesMultiLine === false) { + // Check was turned off. + return; + } + + $error = 'Expected %s after the array opener in a multi line array. Found: %s'; + $code = 'SpaceAfterArrayOpenerMultiLine'; + + $nextNonWhitespace = $phpcsFile->findNext(\T_WHITESPACE, ($opener + 1), null, true); + if ($this->spacesMultiLine === 'newline') { + // Check for a trailing comment after the array opener and allow for it. + if (($tokens[$nextNonWhitespace]['code'] === \T_COMMENT + || isset(Tokens::$phpcsCommentTokens[$tokens[$nextNonWhitespace]['code']]) === true) + && $tokens[$nextNonWhitespace]['line'] === $tokens[$opener]['line'] + ) { + // We found a trailing comment after array opener. Treat that as the opener instead. + $opener = $nextNonWhitespace; + $nextNonWhitespace = $phpcsFile->findNext(\T_WHITESPACE, ($opener + 1), null, true); + } + } + + SpacesFixer::checkAndFix( + $phpcsFile, + $opener, + $nextNonWhitespace, + $this->spacesMultiLine, + $error, + $code, + 'error', + 0, + 'Space after array opener, multi-line array' + ); + + $error = 'Expected %s before the array closer in a multi line array. Found: %s'; + $code = 'SpaceBeforeArrayCloserMultiLine'; + + SpacesFixer::checkAndFix( + $phpcsFile, + $closer, + $phpcsFile->findPrevious(\T_WHITESPACE, ($closer - 1), null, true), + $this->spacesMultiLine, + $error, + $code, + 'error', + 0, + 'Space before array closer, multi-line array' + ); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/NormalizedArrays/Sniffs/Arrays/CommaAfterLastSniff.php b/vendor/phpcsstandards/phpcsextra/NormalizedArrays/Sniffs/Arrays/CommaAfterLastSniff.php new file mode 100644 index 0000000..7384421 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/NormalizedArrays/Sniffs/Arrays/CommaAfterLastSniff.php @@ -0,0 +1,226 @@ + + */ + private $validValues = [ + 'enforce' => true, + 'forbid' => true, + 'skip' => true, + ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.0.0 + * + * @return array + */ + public function register() + { + return Collections::arrayOpenTokensBC(); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + // Validate the property input. Invalid values will result in the check being skipped. + if (isset($this->validValues[$this->singleLine]) === false) { + $this->singleLine = 'skip'; + } + if (isset($this->validValues[$this->multiLine]) === false) { + $this->multiLine = 'skip'; + } + + $openClose = Arrays::getOpenClose($phpcsFile, $stackPtr); + if ($openClose === false) { + // Short list, real square bracket, live coding or parse error. + return; + } + + $tokens = $phpcsFile->getTokens(); + $opener = $openClose['opener']; + $closer = $openClose['closer']; + + $action = $this->singleLine; + $phrase = 'single-line'; + $errorCode = 'SingleLine'; + if ($tokens[$opener]['line'] !== $tokens[$closer]['line']) { + $action = $this->multiLine; + $phrase = 'multi-line'; + $errorCode = 'MultiLine'; + } + + if ($action === 'skip') { + // Nothing to do. + return; + } + + $lastNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($closer - 1), $opener, true); + if ($lastNonEmpty === false || $lastNonEmpty === $opener) { + // Bow out: empty array. + return; + } + + // If the closer is on the same line as the last element, change the error code for multi-line arrays. + if ($errorCode === 'MultiLine' + && $tokens[$lastNonEmpty]['line'] === $tokens[$closer]['line'] + ) { + $errorCode .= 'CloserSameLine'; + } + + $isComma = ($tokens[$lastNonEmpty]['code'] === \T_COMMA); + + $phpcsFile->recordMetric( + $stackPtr, + \sprintf(self::METRIC_NAME, \ucfirst($phrase)), + ($isComma === true ? 'yes' : 'no') + ); + + switch ($action) { + case 'enforce': + if ($isComma === true) { + return; + } + + $error = 'There should be a comma after the last array item in a %s array.'; + $errorCode = 'Missing' . $errorCode; + $data = [$phrase]; + $fix = $phpcsFile->addFixableError($error, $lastNonEmpty, $errorCode, $data); + if ($fix === true) { + $extraContent = ','; + + if (($tokens[$lastNonEmpty]['code'] === \T_END_HEREDOC + || $tokens[$lastNonEmpty]['code'] === \T_END_NOWDOC) + // Check for indentation, if indented, it's a PHP 7.3+ heredoc/nowdoc. + && $tokens[$lastNonEmpty]['content'] === \ltrim($tokens[$lastNonEmpty]['content']) + ) { + // Prevent parse errors in PHP < 7.3 which doesn't support flexible heredoc/nowdoc. + $extraContent = $phpcsFile->eolChar . $extraContent; + } + + $phpcsFile->fixer->addContent($lastNonEmpty, $extraContent); + } + + return; + + case 'forbid': + if ($isComma === false) { + return; + } + + $error = 'A comma after the last array item in a %s array is not allowed.'; + $errorCode = 'Found' . $errorCode; + $data = [$phrase]; + $fix = $phpcsFile->addFixableError($error, $lastNonEmpty, $errorCode, $data); + if ($fix === true) { + $start = $lastNonEmpty; + $end = $lastNonEmpty; + + // Make sure we're not leaving a superfluous blank line behind. + $prevNonWhitespace = $phpcsFile->findPrevious(\T_WHITESPACE, ($lastNonEmpty - 1), $opener, true); + $nextNonWhitespace = $phpcsFile->findNext(\T_WHITESPACE, ($lastNonEmpty + 1), ($closer + 1), true); + if ($prevNonWhitespace !== false + && $tokens[$prevNonWhitespace]['line'] < $tokens[$lastNonEmpty]['line'] + && $nextNonWhitespace !== false + && $tokens[$nextNonWhitespace]['line'] > $tokens[$lastNonEmpty]['line'] + ) { + $start = ($prevNonWhitespace + 1); + } + + $phpcsFile->fixer->beginChangeset(); + + for ($i = $start; $i <= $end; $i++) { + $phpcsFile->fixer->replaceToken($i, ''); + } + + $phpcsFile->fixer->endChangeset(); + } + + return; + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/NormalizedArrays/ruleset.xml b/vendor/phpcsstandards/phpcsextra/NormalizedArrays/ruleset.xml new file mode 100644 index 0000000..69c54a8 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/NormalizedArrays/ruleset.xml @@ -0,0 +1,5 @@ + + + + A ruleset for PHP_CodeSniffer to check arrays for normalized format. + diff --git a/vendor/phpcsstandards/phpcsextra/README.md b/vendor/phpcsstandards/phpcsextra/README.md new file mode 100644 index 0000000..a82fa89 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/README.md @@ -0,0 +1,573 @@ +PHPCSExtra +===================================================== + + + +* [Introduction](#introduction) +* [Minimum Requirements](#minimum-requirements) +* [Installation](#installation) + + [Composer Project-based Installation](#composer-project-based-installation) + + [Composer Global Installation](#composer-global-installation) + + [Updating to a newer version](#updating-to-a-newer-version) +* [Features](#features) +* [Sniffs](#sniffs) + + [Modernize](#modernize) + + [NormalizedArrays](#normalizedarrays) + + [Universal](#universal) +* [Contributing](#contributing) +* [License](#license) + + +Introduction +------------------------------------------- + +PHPCSExtra is a collection of sniffs and standards for use with [PHP_CodeSniffer][phpcs-gh]. + + +Minimum Requirements +------------------------------------------- + +* PHP 5.4 or higher. +* [PHP_CodeSniffer][phpcs-gh] version **3.8.0** or higher. +* [PHPCSUtils][phpcsutils-gh] version **1.0.9** or higher. + + +Installation +------------------------------------------- + +Installing via Composer is highly recommended. + +[Composer](http://getcomposer.org/) will automatically install the project dependencies and register the rulesets from PHPCSExtra and other external standards with PHP_CodeSniffer using the [Composer PHPCS plugin][composer-installer-gh]. + +### Composer Project-based Installation + +Run the following from the root of your project: +```bash +composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true +composer require --dev phpcsstandards/phpcsextra:"^1.2.0" +``` + +### Composer Global Installation + +Alternatively, you may want to install this standard globally: +```bash +composer global config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true +composer global require --dev phpcsstandards/phpcsextra:"^1.2.0" +``` + +### Updating to a newer version + +If you installed PHPCSExtra using either of the above commands, you can update to a newer version as follows: +```bash +# Project local install +composer update phpcsstandards/phpcsextra --with-dependencies + +# Global install +composer global update phpcsstandards/phpcsextra --with-dependencies +``` + +> If your project includes `require[-dev]`s for the `squizlabs/php_codesniffer`, `phpcsstandards/phpcsutils` or +> `dealerdirect/phpcodesniffer-composer-installer` packages in its `composer.json` file, you may need to use +> `--with-all-dependencies` instead of `--with-dependencies`. +> +> :bulb: **Pro-tip**: Unless your project is a PHPCS standard which actually uses any of these packages directly, +> it is recommended to remove these packages from your own `composer.json` file, in favour of letting PHPCSExtra +> (and potential other external PHPCS standards you use), manage the version requirements for these packages. + + +Features +------------------------------------------- + +Once this project is installed, you will see three new rulesets in the list of installed standards when you run `vendor/bin/phpcs -i`: `Modernize`, `NormalizedArrays` and `Universal`. + +* The `Modernize` ruleset is a standard which checks code for modernization opportunaties. +* The `NormalizedArrays` ruleset is a standard to check the formatting of array declarations. +* The `Universal` ruleset is **NOT** a standard, but a sniff collection. + It should **NOT** be included in custom rulesets as a standard as it contains contradictory rules. + Instead include individual sniffs from this standard in a custom project/company ruleset to use them. + + +Sniffs +------------------------------------------- + +**Legend**: +* :wrench: = Includes auto-fixer. + _Use the `phpcbf` command to run the fixers._ +* :bar_chart: = Includes metrics. + _Use `phpcs` with `--report=info` to see the metrics._ +* :books: = Includes CLI documentation. + _Use `phpcs` with `--generator=Text` to see the documentation._ + + +### Modernize + +#### `Modernize.FunctionCalls.Dirname` :wrench: :books: + +This sniff will detect and auto-fix two typical code modernizations which can be made related to the `dirname()` function: +1. Since PHP 5.3, calls to `dirname(__FILE__)` can be replaced by `__DIR__`. + Errorcode: `Modernize.FunctionCalls.Dirname.FileConstant`. +2. Since PHP 7.0, nested function calls to `dirname()` can be changed to use the `$levels` parameter. + Errorcode: `Modernize.FunctionCalls.Dirname.Nested`. + +If a [`php_version` configuration option][php_version-config] has been passed to PHPCS using either `--config-set` or `--runtime-set`, it will be respected by the sniff. +In effect, this means that the sniff will only report on modernizations which can be applied for the PHP version as configured. + + +### NormalizedArrays + +#### `NormalizedArrays.Arrays.ArrayBraceSpacing` :wrench: :bar_chart: :books: + +Enforce consistent spacing for the open/close braces of array declarations. + +The sniff allows for having different settings for: +- Space between the array keyword and the open parenthesis for long arrays via the `keywordSpacing` property. + Accepted values: (int) number of spaces or `false` to turn this check off. Defaults to `0` spaces. +- Spaces on the inside of the braces for empty arrays via the `spacesWhenEmpty` property. + Accepted values: (string) `newline`, (int) number of spaces or `false` to turn this check off. Defaults to `0` spaces. +- Spaces on the inside of the braces for single-line arrays via the `spacesSingleLine` property; + Accepted values: (int) number of spaces or `false` to turn this check off. Defaults to `0` spaces. +- Spaces on the inside of the braces for multi-line arrays via the `spacesMultiLine` property. + Accepted values: (string) `newline`, (int) number of spaces or `false` to turn this check off. Defaults to `newline`. + +Note: if any of the above properties are set to `newline`, it is recommended to also include an array indentation sniff. This sniff will not handle the indentation. + +#### `NormalizedArrays.Arrays.CommaAfterLast` :wrench: :bar_chart: :books: + +Enforce/forbid a comma after the last item in an array declaration. + +By default, this sniff will: +* Forbid a comma after the last array item for single-line arrays. +* Enforce a comma after the last array item for multi-line arrays. + +This can be changed for each type or array individually by setting the `singleLine` and/or `multiLine` properties in a custom ruleset. + +Use any of the following values to change the properties: `enforce`, `forbid` or `skip` to not check the comma after the last array item for a particular type of array. + +The default for the `singleLine` property is `forbid`. The default for the `multiLine` property is `enforce`. + + +### Universal + +#### `Universal.Arrays.DisallowShortArraySyntax` :wrench: :bar_chart: :books: + +Disallow short array syntax. + +In contrast to the PHPCS native `Generic.Arrays.DisallowShortArraySyntax` sniff, this sniff will ignore short list syntax and not cause parse errors when the fixer is used. + +#### `Universal.Arrays.DuplicateArrayKey` :books: + +Detects duplicate array keys in array declarations. + +The sniff will make a distinction between keys which will be duplicate in all PHP version and (numeric) keys which will only be a duplicate key in [PHP < 8.0 or PHP >= 8.0][php-rfc-negative_array_index]. + +If a [`php_version` configuration option][php_version-config] has been passed to PHPCS using either `--config-set` or `--runtime-set`, it will be respected by the sniff and only report duplicate keys for the configured PHP version. + +[php-rfc-negative_array_index]: https://wiki.php.net/rfc/negative_array_index + +#### `Universal.Arrays.MixedArrayKeyTypes` :books: + +Best practice sniff: don't use a mix of integer and string keys for array items. + +#### `Universal.Arrays.MixedKeyedUnkeyedArray` :books: + +Best practice sniff: don't use a mix of keyed and unkeyed array items. + +#### `Universal.Classes.DisallowAnonClassParentheses` :wrench: :bar_chart: :books: + +Disallow the use of parentheses when declaring an anonymous class without passing parameters. + +#### `Universal.Classes.RequireAnonClassParentheses` :wrench: :bar_chart: :books: + +Require the use of parentheses when declaring an anonymous class, whether parameters are passed or not. + +#### `Universal.Classes.DisallowFinalClass` :wrench: :bar_chart: :books: + +Disallow classes being declared `final`. + +#### `Universal.Classes.RequireFinalClass` :wrench: :bar_chart: :books: + +Require all non-`abstract` classes to be declared `final`. + +:warning: **Warning**: the auto-fixer for this sniff _may_ have unintended side-effects for applications and should be used with care! +This is considered a **_risky_ fixer**. + +#### `Universal.Classes.ModifierKeywordOrder` :wrench: :bar_chart: :books: + +Require a consistent modifier keyword order for class declarations. + +* This sniff contains an `order` property to specify the preferred order. + Accepted values: (string) `'extendability readonly'`|`'readonly extendability'`. Defaults to `'extendability readonly'`. + +#### `Universal.CodeAnalysis.ConstructorDestructorReturn` :wrench: :books: + +* Disallows return type declarations on constructor/destructor methods - error code: `ReturnTypeFound`, auto-fixable. +* Discourages constructor/destructor methods returning a value - error code: `ReturnValueFound`. + +If a [`php_version` configuration option][php_version-config] has been passed to PHPCS using either `--config-set` or `--runtime-set`, it will be respected by the sniff. +In effect, this means that the sniff will only report on PHP4-style constructors if the configured PHP version is less than 8.0. + +#### `Universal.CodeAnalysis.ForeachUniqueAssignment` :wrench: :books: + +Detects `foreach` control structures which use the same variable for both the key as well as the value assignment as this will lead to unexpected - and most likely unintended - behaviour. + +Note: The fixer will maintain the existing behaviour of the code. This may not be the _intended_ behaviour. + +#### `Universal.CodeAnalysis.NoDoubleNegative` :wrench: :books: + +Detects double negation `!!` in code, which is effectively the same as a boolean cast, but with a much higher cognitive load. +Also detects triple negation `!!!`, which is effectively the same as a single negation. + +The sniff has modular error codes to allow for disabling individual checks. The error codes are: `FoundDouble`, `FoundDoubleWithInstanceof` (not auto-fixable) and `FoundTriple`. + +#### `Universal.CodeAnalysis.NoEchoSprintf` :wrench: :books: + +Detects use of the inefficient `echo [v]sprintf(...);` combi. Use `[v]printf()` instead. + +#### `Universal.CodeAnalysis.StaticInFinalClass` :wrench: :books: + +Detects using `static` instead of `self` in OO constructs which are `final`. + +* The sniff has modular error codes to allow for making exceptions based on the type of use for `static`. + The available error codes are: `ReturnType`, `InstanceOf`, `NewInstance`, `ScopeResolution`. + +#### `Universal.Constants.LowercaseClassResolutionKeyword` :wrench: :bar_chart: :books: + +Enforce that the `class` keyword when used for class name resolution, i.e. `::class`, is in lowercase. + +#### `Universal.Constants.ModifierKeywordOrder` :wrench: :bar_chart: :books: + +Require a consistent modifier keyword order for OO constant declarations. + +* This sniff contains an `order` property to specify the preferred order. + Accepted values: (string) `'final visibility'`|`'visibility final'`. Defaults to `'final visibility'`. + +#### `Universal.Constants.UppercaseMagicConstants` :wrench: :bar_chart: :books: + +Enforce uppercase when using PHP native magic constants, like `__FILE__` et al. + +#### `Universal.ControlStructures.DisallowAlternativeSyntax` :wrench: :bar_chart: :books: + +Disallow using the alternative syntax for control structures. + +* This sniff contains an `allowWithInlineHTML` property to allow alternative syntax when inline HTML is used within the control structure. In all other cases, the use of the alternative syntax will still be disallowed. + Accepted values: (bool) `true`|`false`. Defaults to `false`. +* The sniff has modular error codes to allow for making exceptions based on specific control structures and/or specific control structures in combination with inline HTML. + The error codes follow the following pattern: `Found[ControlStructure][WithInlineHTML]`. Examples: `FoundIf`, `FoundSwitchWithInlineHTML`. + +#### `Universal.ControlStructures.DisallowLonelyIf` :wrench: :books: + +Disallow `if` statements as the only statement in an `else` block. + +Note: This sniff will not fix the indentation of the "inner" code. +It is strongly recommended to run this sniff together with the `Generic.WhiteSpace.ScopeIndent` sniff to get the correct indentation. + +#### `Universal.ControlStructures.IfElseDeclaration` :wrench: :bar_chart: :books: + +Verify that else(if) statements with braces are on a new line. + +#### `Universal.Files.SeparateFunctionsFromOO` :bar_chart: :books: + +Enforce for a file to either declare (global/namespaced) functions or declare OO structures, but not both. + +* Nested function declarations, i.e. functions declared within a function/method will be disregarded for the purposes of this sniff. + The same goes for anonymous classes, closures and arrow functions. +* Note: This sniff has no opinion on side effects. If you want to sniff for those, use the PHPCS native `PSR1.Files.SideEffects` sniff. +* Also note: This sniff has no opinion on multiple OO structures being declared in one file. + If you want to sniff for that, use the PHPCS native `Generic.Files.OneObjectStructurePerFile` sniff. + +#### `Universal.FunctionDeclarations.NoLongClosures` :bar_chart: :books: + +Detects "long" closures and recommends using a named function instead. + +The sniff is configurable by setting any of the following properties in a custom ruleset: +* `recommendedLines` (int): determines when a warning will be thrown. + Defaults to `5`, meaning a warning with the errorcode `ExceedsRecommended` will be thrown if the closure is more than 5 lines long. +* `maxLines` (int): determines when an error will be thrown. + Defaults to `8`, meaning that an error with the errorcode `ExceedsMaximum` will be thrown if the closure is more than 8 lines long. +* `ignoreCommentLines` (bool): whether or not comment-only lines should be ignored for the lines count. + Defaults to `true`. +* `ignoreEmptyLines` (bool): whether or not blank lines should be ignored for the lines count. + Defaults to `true`. + +#### `Universal.FunctionDeclarations.RequireFinalMethodsInTraits` :wrench: :bar_chart: :books: + +Enforce non-private, non-abstract methods in traits to be declared as `final`. + +The available error codes are: `NonFinalMethodFound` and `NonFinalMagicMethodFound`. + +#### `Universal.Lists.DisallowLongListSyntax` :wrench: :books: + +Disallow the use of long `list`s. + +> For metrics about the use of long lists vs short lists, please use the `Universal.Lists.DisallowShortListSyntax` sniff. + +#### `Universal.Lists.DisallowShortListSyntax` :wrench: :bar_chart: :books: + +Disallow the use of short lists. + +#### `Universal.Namespaces.DisallowDeclarationWithoutName` :bar_chart: :books: + +Disallow namespace declarations without a namespace name. + +This sniff only applies to namespace declarations using the curly brace syntax. + +#### `Universal.Namespaces.DisallowCurlyBraceSyntax` :bar_chart: :books: + +Disallow the use of the alternative namespace declaration syntax using curly braces. + +#### `Universal.Namespaces.EnforceCurlyBraceSyntax` :bar_chart: :books: + +Enforce the use of the alternative namespace syntax using curly braces. + +#### `Universal.Namespaces.OneDeclarationPerFile` :books: + +Disallow the use of multiple namespaces within a file. + +#### `Universal.NamingConventions.NoReservedKeywordParameterNames` :books: + +Disallow function parameters using reserved keywords as names, as this can quickly become confusing when people use them in function calls using named parameters + +* The sniff has modular error codes to allow for making exceptions for specific keywords. + The error codes follow the following pattern: `[keyword]Found`. + +#### `Universal.OOStructures.AlphabeticExtendsImplements` :wrench: :bar_chart: :books: + +Enforce that the names used in a class/enum "implements" statement or an interface "extends" statement are listed in alphabetic order. + +* This sniff contains a `orderby` property to determine the sort order to use for the statement. + If all names used are unqualified, the sort order won't make a difference. + However, if one or more of the names are partially or fully qualified, the chosen sort order will determine how the sorting between unqualified, partially and fully qualified names is handled. + The sniff supports two sort order options: + - _'name'_ : sort by the interface name only (default); + - _'full'_ : sort by the full name as used in the statement (without leading backslash). + In both cases, the sorting will be done using natural sort, case-insensitive. +* The sniff has modular error codes to allow for selective inclusion/exclusion: + - `ImplementsWrongOrder` - for "class implements" statements. + - `ImplementsWrongOrderWithComments` - for "class implements" statements interlaced with comments. These will not be auto-fixed. + - `ExtendsWrongOrder` - for "interface extends" statements. + - `ExtendsWrongOrderWithComments` - for "interface extends" statements interlaced with comments. These will not be auto-fixed. +* When fixing, the existing spacing between the names in an `implements`/`extends` statement will not be maintained. + The fixer will separate each name with a comma and one space. + If alternative formatting is desired, a sniff which will check and fix the formatting should be added to the ruleset. + +#### `Universal.Operators.ConcatPosition` :wrench: :bar_chart: :books: + +Enforce that the concatenation operator for multi-line concatenations is in a preferred position, either always at the start of the next line or always at the end of the previous line. + +* This sniff contains an `allowOnly` property to set the preferred position for the operator. + Accepted values: (string) `"start"` or `"end"`. Defaults to `"start"`. +* Note: mid-line concatenation is still allowed and will not be flagged by this sniff. + +#### `Universal.Operators.DisallowLogicalAndOr` :bar_chart: :books: + +Enforce the use of the boolean `&&` and `||` operators instead of the logical `and`/`or` operators. + +:information_source: Note: as the [operator precedence](https://www.php.net/language.operators.precedence) of the logical operators is significantly lower than the operator precedence of boolean operators, this sniff does not contain an auto-fixer. + +#### `Universal.Operators.DisallowShortTernary` :bar_chart: :books: + +Disallow the use of short ternaries `?:`. + +While short ternaries are useful when used correctly, the principle of them is often misunderstood and they are more often than not used incorrectly, leading to hard to debug issues and/or PHP warnings/notices. + +#### `Universal.Operators.DisallowStandalonePostIncrementDecrement` :wrench: :bar_chart: :books: + +* Disallow the use of post-in/decrements in stand-alone statements - error codes: `PostDecrementFound` and `PostIncrementFound`. + Using pre-in/decrement is more in line with the principle of least astonishment and prevents bugs when code gets moved around at a later point in time. +* Discourages the use of multiple increment/decrement operators in a stand-alone statement - error code: `MultipleOperatorsFound`. + +#### `Universal.Operators.StrictComparisons` :wrench: :bar_chart: :books: + +Enforce the use of strict comparisons. + +:warning: **Warning**: the auto-fixer for this sniff _may_ cause bugs in applications and should be used with care! +This is considered a **_risky_ fixer**. + +#### `Universal.Operators.TypeSeparatorSpacing` :wrench: :bar_chart: :books: + +Enforce no spaces around the union type and intersection type operators. + +The available error codes are: `UnionTypeSpacesBefore`, `UnionTypeSpacesAfter`, `IntersectionTypeSpacesBefore`, `IntersectionTypeSpacesAfter`. + +#### `Universal.PHP.LowercasePHPTag` :wrench: :bar_chart: :books: + +Enforces that the "PHP" in a PHP open tag is lowercase. + +#### `Universal.PHP.OneStatementInShortEchoTag` :wrench: :books: + +Disallow short open echo tags ` _This sniff is especially useful for tab-indentation based standards which use the `Generic.Whitespace.DisallowSpaceIndent` sniff to enforce this._ +> +> **DO** make sure to set the PHPCS native `tab-width` configuration for the best results. +> ```xml +> +> ``` +> +> The PHPCS native `Generic.Whitespace.DisallowTabIndent` sniff (used for space-based standards) oversteps its reach and silently does mid-line tab to space replacements as well. +> However, the sister-sniff `Generic.Whitespace.DisallowSpaceIndent` leaves mid-line tabs/spaces alone. +> This sniff fills that gap. + +#### `Universal.WhiteSpace.PrecisionAlignment` :wrench: :books: + +Enforce code indentation to always be a multiple of a tabstop, i.e. disallow precision alignment. + +Note: +* This sniff does not concern itself with tabs versus spaces. + It is recommended to use the sniff in combination with the PHPCS native `Generic.WhiteSpace.DisallowTabIndent` or the `Generic.WhiteSpace.DisallowSpaceIndent` sniff. +* When using this sniff with tab-based standards, please ensure that the `tab-width` is set and either don't set the `$indent` property or set it to the tab-width (or a multiple thereof). +* The fixer works based on "best guess" and may not always result in the desired indentation. Combine this sniff with the `Generic.WhiteSpace.ScopeIndent` sniff for more precise indentation fixes. + +The behaviour of the sniff is customizable via the following properties: +* `indent`: the indent used for the codebase. + Accepted values: (int|null) number of spaces. Defaults to `null`. + If this property is not set, the sniff will look to the `--tab-width` CLI value. + If that also isn't set, the default tab-width of `4` will be used. +* `ignoreAlignmentBefore`: allows for providing a list of token names for which (preceding) precision alignment should be ignored. + Accepted values: (array) token constant names. Defaults to an empty array. + Usage example: + ```xml + + + + + + + + + + + + ``` +* `ignoreBlankLines`: whether or not potential trailing whitespace on otherwise blank lines should be examined or ignored. + It is recommended to only set this to `false` if the standard including this sniff does not include the `Squiz.WhiteSpace.SuperfluousWhitespace` sniff (which is included in most standards). + Accepted values: (bool)`true`|`false`. Defaults to `true`. + + +Contributing +------- +Contributions to this project are welcome. Clone the repo, branch off from `develop`, make your changes, commit them and send in a pull request. + +If unsure whether the changes you are proposing would be welcome, open an issue first to discuss your proposal. + +License +------- +This code is released under the [GNU Lesser General Public License (LGPLv3)](LICENSE). + + +[phpcsextra-packagist]: https://packagist.org/packages/phpcsstandards/phpcsextra +[gha-qa-results]: https://github.com/PHPCSStandards/PHPCSExtra/actions/workflows/basics.yml +[gha-test-results]: https://github.com/PHPCSStandards/PHPCSExtra/actions/workflows/test.yml + +[phpcs-gh]: https://github.com/PHPCSStandards/PHP_CodeSniffer +[phpcsutils-gh]: https://github.com/PHPCSStandards/PHPCSUtils +[composer-installer-gh]: https://github.com/PHPCSStandards/composer-installer + +[php_version-config]: https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki/Configuration-Options#setting-the-php-version diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Arrays/DisallowShortArraySyntaxStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Arrays/DisallowShortArraySyntaxStandard.xml new file mode 100644 index 0000000..4a84b53 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Arrays/DisallowShortArraySyntaxStandard.xml @@ -0,0 +1,27 @@ + + + + + + + + array( + 'foo' => 'bar', +); + ]]> + + + [ + 'foo' => 'bar', +]; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Arrays/DuplicateArrayKeyStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Arrays/DuplicateArrayKeyStandard.xml new file mode 100644 index 0000000..00f9b7b --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Arrays/DuplicateArrayKeyStandard.xml @@ -0,0 +1,44 @@ + + + + + + + + 'foo' => 22, + 'bar' => 25, + 'baz' => 28, +); + +$args = array( + 22, + 25, + 2 => 28, +); + ]]> + + + 'foo' => 22, + 'bar' => 25, + 'bar' => 28, +); + +$args = array( + 22, + 25, + 1 => 28, +); + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Arrays/MixedArrayKeyTypesStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Arrays/MixedArrayKeyTypesStandard.xml new file mode 100644 index 0000000..f7efdda --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Arrays/MixedArrayKeyTypesStandard.xml @@ -0,0 +1,40 @@ + + + + + + + + 'foo' => 22, + 'bar' => 25, +); + +$args = array( + 0 => 22, + 1 => 25, +); + ]]> + + + 22, + 25, +); + +$args = array( + 'foo' => 22, + 12 => 25, +); + + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Arrays/MixedKeyedUnkeyedArrayStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Arrays/MixedKeyedUnkeyedArrayStandard.xml new file mode 100644 index 0000000..79897c1 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Arrays/MixedKeyedUnkeyedArrayStandard.xml @@ -0,0 +1,31 @@ + + + + + + + + 'foo' => 22, + 'bar' => 25, +); + +$args = array(22, 25); + ]]> + + + 22, + 25, +); + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/DisallowAnonClassParenthesesStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/DisallowAnonClassParenthesesStandard.xml new file mode 100644 index 0000000..ebfd7c6 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/DisallowAnonClassParenthesesStandard.xml @@ -0,0 +1,24 @@ + + + + + + + + {}; +$anon = new class($param) {}; + ]]> + + + () {}; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/DisallowFinalClassStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/DisallowFinalClassStandard.xml new file mode 100644 index 0000000..fd7ddb6 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/DisallowFinalClassStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + class Foo {} +abstract class Bar implements MyInterface {} + ]]> + + + final class Foo {} +final class Bar extends MyAbstract {} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/ModifierKeywordOrderStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/ModifierKeywordOrderStandard.xml new file mode 100644 index 0000000..a80ae0d --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/ModifierKeywordOrderStandard.xml @@ -0,0 +1,27 @@ + + + + + + + + final readonly class Foo {} +abstract readonly class Bar {} + ]]> + + + readonly final class Foo {} +readonly abstract class Bar {} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/RequireAnonClassParenthesesStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/RequireAnonClassParenthesesStandard.xml new file mode 100644 index 0000000..48fa148 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/RequireAnonClassParenthesesStandard.xml @@ -0,0 +1,23 @@ + + + + + + + + () {}; + ]]> + + + {}; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/RequireFinalClassStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/RequireFinalClassStandard.xml new file mode 100644 index 0000000..107b8f4 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Classes/RequireFinalClassStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + final class Foo {} +final class Bar extends MyAbstract {} + ]]> + + + class Foo {} +abstract class Bar implements MyInterface {} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/ConstructorDestructorReturnStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/ConstructorDestructorReturnStandard.xml new file mode 100644 index 0000000..b44b477 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/ConstructorDestructorReturnStandard.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + : int {} +} + ]]> + + + + + + + + + + + + $this; + } + + public function __destruct() { + // Do something. + return false; + } +} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/ForeachUniqueAssignmentStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/ForeachUniqueAssignmentStandard.xml new file mode 100644 index 0000000..6f9676e --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/ForeachUniqueAssignmentStandard.xml @@ -0,0 +1,26 @@ + + + + + + + + $v ) {} + ]]> + + + $k ) {} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/NoDoubleNegativeStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/NoDoubleNegativeStandard.xml new file mode 100644 index 0000000..953149b --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/NoDoubleNegativeStandard.xml @@ -0,0 +1,27 @@ + + + + + + + + ! $b; + +if((bool) callMe($a)) {} + ]]> + + + ! ! $b; + +if(! ! ! callMe($a)) {} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/NoEchoSprintfStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/NoEchoSprintfStandard.xml new file mode 100644 index 0000000..914eb65 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/NoEchoSprintfStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + printf('text %s text', $var); +echo callMe('text %s text', $var); + ]]> + + + echo sprintf('text %s text', $var); +echo vsprintf('text %s text', [$var]); + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/StaticInFinalClassStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/StaticInFinalClassStandard.xml new file mode 100644 index 0000000..5f9f45b --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/CodeAnalysis/StaticInFinalClassStandard.xml @@ -0,0 +1,43 @@ + + + + + + + + self + { + $var = self::functionCall(); + $var = $obj instanceof self; + $var = new self; + } +} + ]]> + + + static|false { + $var = static::$prop; + $var = $obj instanceof static; + $var = new static(); + } +}; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Constants/LowercaseClassResolutionKeywordStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Constants/LowercaseClassResolutionKeywordStandard.xml new file mode 100644 index 0000000..7b61c8f --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Constants/LowercaseClassResolutionKeywordStandard.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + MyClass::CLASS; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Constants/ModifierKeywordOrderStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Constants/ModifierKeywordOrderStandard.xml new file mode 100644 index 0000000..f42483a --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Constants/ModifierKeywordOrderStandard.xml @@ -0,0 +1,30 @@ + + + + + + + + final public const FOO = 'foo'; +} + ]]> + + + protected final const BAR = 'foo'; +} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Constants/UppercaseMagicConstantsStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Constants/UppercaseMagicConstantsStandard.xml new file mode 100644 index 0000000..2a9583f --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Constants/UppercaseMagicConstantsStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + __LINE__; +include __DIR__ . '/file.php'; + ]]> + + + __NameSpace__; +include dirname(__file__) . '/file.php'; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/ControlStructures/DisallowAlternativeSyntaxStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/ControlStructures/DisallowAlternativeSyntaxStandard.xml new file mode 100644 index 0000000..4be7e6d --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/ControlStructures/DisallowAlternativeSyntaxStandard.xml @@ -0,0 +1,35 @@ + + + + + + + + { + $var = 1; +} + +while (++$i < 10) { + echo $i; +} + ]]> + + + : + $var = 1; +endif; + +while (++$i < 10): + echo $i; +endwhile; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/ControlStructures/DisallowLonelyIfStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/ControlStructures/DisallowLonelyIfStandard.xml new file mode 100644 index 0000000..88d0922 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/ControlStructures/DisallowLonelyIfStandard.xml @@ -0,0 +1,49 @@ + + + + + + + + elseif ($bar) { + // ... +} + +if ($foo) { + // ... +} else { + if ($bar) { + // ... + } + + doSomethingElse(); + +} + + ]]> + + + if ($bar) { + // ... + } else { + // ... + } +} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/ControlStructures/IfElseDeclarationStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/ControlStructures/IfElseDeclarationStandard.xml new file mode 100644 index 0000000..b1506f6 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/ControlStructures/IfElseDeclarationStandard.xml @@ -0,0 +1,37 @@ + + + + + + + + +elseif ($bar) { + $var = 2; +} +else { + $var = 3; +} + ]]> + + + elseif ($bar) { + $var = 2; +} else { + $var = 3; +} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Files/SeparateFunctionsFromOOStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Files/SeparateFunctionsFromOOStandard.xml new file mode 100644 index 0000000..db0a267 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Files/SeparateFunctionsFromOOStandard.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/FunctionDeclarations/NoLongClosuresStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/FunctionDeclarations/NoLongClosuresStandard.xml new file mode 100644 index 0000000..dc84e0b --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/FunctionDeclarations/NoLongClosuresStandard.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/FunctionDeclarations/RequireFinalMethodsInTraitsStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/FunctionDeclarations/RequireFinalMethodsInTraitsStandard.xml new file mode 100644 index 0000000..4b2622d --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/FunctionDeclarations/RequireFinalMethodsInTraitsStandard.xml @@ -0,0 +1,33 @@ + + + + + + + + final public function bar() {} + final public static function baz() {} + + // Also valid (out of scope): + protected abstract function overload() {} + private function okay() {} +} + ]]> + + + public function bar() {} + protected static function baz() {} +} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Lists/DisallowLongListSyntaxStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Lists/DisallowLongListSyntaxStandard.xml new file mode 100644 index 0000000..35475ea --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Lists/DisallowLongListSyntaxStandard.xml @@ -0,0 +1,23 @@ + + + + + + + + [$a, $b] = $array; + ]]> + + + list($a, $b) = $array; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Lists/DisallowShortListSyntaxStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Lists/DisallowShortListSyntaxStandard.xml new file mode 100644 index 0000000..214b686 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Lists/DisallowShortListSyntaxStandard.xml @@ -0,0 +1,23 @@ + + + + + + + + list($a, $b) = $array; + ]]> + + + [$a, $b] = $array; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Namespaces/DisallowCurlyBraceSyntaxStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Namespaces/DisallowCurlyBraceSyntaxStandard.xml new file mode 100644 index 0000000..95174d7 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Namespaces/DisallowCurlyBraceSyntaxStandard.xml @@ -0,0 +1,27 @@ + + + + + + + + ; + +// Code + ]]> + + + { + // Code. +} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Namespaces/DisallowDeclarationWithoutNameStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Namespaces/DisallowDeclarationWithoutNameStandard.xml new file mode 100644 index 0000000..465ea55 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Namespaces/DisallowDeclarationWithoutNameStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + Vendor\Name { +} + ]]> + + + { +} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Namespaces/EnforceCurlyBraceSyntaxStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Namespaces/EnforceCurlyBraceSyntaxStandard.xml new file mode 100644 index 0000000..8e1892c --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Namespaces/EnforceCurlyBraceSyntaxStandard.xml @@ -0,0 +1,27 @@ + + + + + + + + { + // Code. +} + ]]> + + + ; + +// Code + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Namespaces/OneDeclarationPerFileStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Namespaces/OneDeclarationPerFileStandard.xml new file mode 100644 index 0000000..6aa0f15 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Namespaces/OneDeclarationPerFileStandard.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + namespace Vendor\Project\Sub\B { +} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/NamingConventions/NoReservedKeywordParameterNamesStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/NamingConventions/NoReservedKeywordParameterNamesStandard.xml new file mode 100644 index 0000000..d44935c --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/NamingConventions/NoReservedKeywordParameterNamesStandard.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/OOStructures/AlphabeticExtendsImplementsStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/OOStructures/AlphabeticExtendsImplementsStandard.xml new file mode 100644 index 0000000..f22a719 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/OOStructures/AlphabeticExtendsImplementsStandard.xml @@ -0,0 +1,27 @@ + + + + + + + + Bar, Foo +{ +} + ]]> + + + Foo, Bar +{ +} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/ConcatPositionStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/ConcatPositionStandard.xml new file mode 100644 index 0000000..4d2761a --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/ConcatPositionStandard.xml @@ -0,0 +1,31 @@ + + + + + + + + . $b . 'text' + . $c; + ]]> + + + . + $b . 'text' + . $c; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/DisallowLogicalAndOrStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/DisallowLogicalAndOrStandard.xml new file mode 100644 index 0000000..4e39878 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/DisallowLogicalAndOrStandard.xml @@ -0,0 +1,30 @@ + + + + + + + + && $var > 10) {} + +if (empty($var) || $var < 0) {} + ]]> + + + and $var > 10) {} + +if (empty($var) or $var < 0) {} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/DisallowShortTernaryStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/DisallowShortTernaryStandard.xml new file mode 100644 index 0000000..0ef2ea3 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/DisallowShortTernaryStandard.xml @@ -0,0 +1,26 @@ + + + + + + + + ? $a : 'default'; + ]]> + + + ?: 'default'; +echo $a ? : 'default'; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/DisallowStandalonePostIncrementDecrementStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/DisallowStandalonePostIncrementDecrementStandard.xml new file mode 100644 index 0000000..0678003 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/DisallowStandalonePostIncrementDecrementStandard.xml @@ -0,0 +1,44 @@ + + + + + + + + ++$i; +--$j; + ]]> + + + ++; +$j--; + ]]> + + + + + + + + ++$i; + ]]> + + + --$i++++; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/StrictComparisonsStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/StrictComparisonsStandard.xml new file mode 100644 index 0000000..b4507ed --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/StrictComparisonsStandard.xml @@ -0,0 +1,29 @@ + + + + + + + + === 'text') {} + +if ($var !== true) {} + ]]> + + + == 'text') {} + +if ($var != true) {} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/TypeSeparatorSpacingStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/TypeSeparatorSpacingStandard.xml new file mode 100644 index 0000000..3d3c328 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/Operators/TypeSeparatorSpacingStandard.xml @@ -0,0 +1,33 @@ + + + + + + + + |string $paramA, + TypeA&TypeB $paramB +): int|false {} + ]]> + + + | string $paramA, + TypeA & TypeB $paramB +): int + | + false {} + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/PHP/LowercasePHPTagStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/PHP/LowercasePHPTagStandard.xml new file mode 100644 index 0000000..261f93c --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/PHP/LowercasePHPTagStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/PHP/OneStatementInShortEchoTagStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/PHP/OneStatementInShortEchoTagStandard.xml new file mode 100644 index 0000000..103ca13 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/PHP/OneStatementInShortEchoTagStandard.xml @@ -0,0 +1,41 @@ + + + + + + + + + ]]> + + + $text; echo $moreText; ?> + ]]> + + + + + +echo $text; +echo $moreText; +?> + ]]> + + + $text; +echo $moreText; +?> + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/DisallowMixedGroupUseStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/DisallowMixedGroupUseStandard.xml new file mode 100644 index 0000000..2b930f2 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/DisallowMixedGroupUseStandard.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/DisallowUseClassStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/DisallowUseClassStandard.xml new file mode 100644 index 0000000..1699d9d --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/DisallowUseClassStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/DisallowUseConstStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/DisallowUseConstStandard.xml new file mode 100644 index 0000000..0dbec70 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/DisallowUseConstStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/DisallowUseFunctionStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/DisallowUseFunctionStandard.xml new file mode 100644 index 0000000..f3cb1f6 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/DisallowUseFunctionStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/KeywordSpacingStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/KeywordSpacingStandard.xml new file mode 100644 index 0000000..55d430e --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/KeywordSpacingStandard.xml @@ -0,0 +1,29 @@ + + + + + + + + function strpos; +use const PHP_EOL as MY_EOL; + ]]> + + + function strpos; +use + const + PHP_EOL + as + MY_EOL; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/LowercaseFunctionConstStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/LowercaseFunctionConstStandard.xml new file mode 100644 index 0000000..66acd83 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/LowercaseFunctionConstStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + function strpos; +use const PHP_EOL; + ]]> + + + Function strpos; +use CONST PHP_EOL; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/NoLeadingBackslashStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/NoLeadingBackslashStandard.xml new file mode 100644 index 0000000..ff0b786 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/NoLeadingBackslashStandard.xml @@ -0,0 +1,23 @@ + + + + + + + + use Package\ClassName; + ]]> + + + \Package\ClassName; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/NoUselessAliasesStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/NoUselessAliasesStandard.xml new file mode 100644 index 0000000..39bb90d --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/UseStatements/NoUselessAliasesStandard.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/WhiteSpace/AnonClassKeywordSpacingStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/WhiteSpace/AnonClassKeywordSpacingStandard.xml new file mode 100644 index 0000000..1770f74 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/WhiteSpace/AnonClassKeywordSpacingStandard.xml @@ -0,0 +1,31 @@ + + + + + + + + class($param) +{ + public function __construct($p) {} +}; + ]]> + + + class ($param) +{ + public function __construct($p) {} +}; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/WhiteSpace/CommaSpacingStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/WhiteSpace/CommaSpacingStandard.xml new file mode 100644 index 0000000..503ae6f --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/WhiteSpace/CommaSpacingStandard.xml @@ -0,0 +1,94 @@ + + + + + + + + , $param2, $param3); + +function_call( + $param1, + $param2, + $param3 +); + +$array = array($item1, $item2, $item3); +$array = [ + $item1, + $item2, +]; + +list(, $a, $b,,) = $array; +list( + , + $a, + $b, +) = $array; + ]]> + + + , $param2,$param3); + +function_call( + $a + ,$b + ,$c +); + +$array = array($item1,$item2 , $item3); +$array = [ + $item1, + $item2 , +]; + +list( ,$a, $b ,,) = $array; +list( + , + $a, + $b , +) = $array; + ]]> + + + + + + + + , // Comment. + $param2, /* Comment. */ +); + ]]> + + + , + $param2 /* Comment. */, +); + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/WhiteSpace/DisallowInlineTabsStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/WhiteSpace/DisallowInlineTabsStandard.xml new file mode 100644 index 0000000..b259ab4 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/WhiteSpace/DisallowInlineTabsStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + [space]= 'text'; +$text[space][space]= 'more text'; + ]]> + + + [tab]= 'text'; +$text[tab]= 'more text'; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Docs/WhiteSpace/PrecisionAlignmentStandard.xml b/vendor/phpcsstandards/phpcsextra/Universal/Docs/WhiteSpace/PrecisionAlignmentStandard.xml new file mode 100644 index 0000000..cde20f2 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Docs/WhiteSpace/PrecisionAlignmentStandard.xml @@ -0,0 +1,29 @@ + + + + + + + + [space][space][space][space]$foo = 'bar'; + +[tab]$foo = 'bar'; + ]]> + + + [space][space]$foo = 'bar'; + +[tab][space]$foo = 'bar'; + ]]> + + + diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Helpers/DummyTokenizer.php b/vendor/phpcsstandards/phpcsextra/Universal/Helpers/DummyTokenizer.php new file mode 100644 index 0000000..7c172e3 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Helpers/DummyTokenizer.php @@ -0,0 +1,60 @@ +eolChar = $eolChar; + $this->config = $config; + } + + /** + * Creates an array of tokens when given some content. + * + * @param string $string The string to tokenize. + * + * @return array> + */ + protected function tokenize($string) + { + return []; + } + + /** + * Performs additional processing after main tokenizing. + * + * @return void + */ + protected function processAdditional() + { + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Arrays/DisallowShortArraySyntaxSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Arrays/DisallowShortArraySyntaxSniff.php new file mode 100644 index 0000000..829daaa --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Arrays/DisallowShortArraySyntaxSniff.php @@ -0,0 +1,89 @@ + + */ + public function register() + { + return Collections::arrayOpenTokensBC(); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + if ($tokens[$stackPtr]['code'] === \T_ARRAY) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no'); + return; + } + + if (Arrays::isShortArray($phpcsFile, $stackPtr) === false) { + // Square brackets, but not a short array. Probably short list or real square brackets. + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes'); + + $error = 'Short array syntax is not allowed'; + $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found'); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->replaceToken($tokens[$stackPtr]['bracket_opener'], 'array('); + $phpcsFile->fixer->replaceToken($tokens[$stackPtr]['bracket_closer'], ')'); + $phpcsFile->fixer->endChangeset(); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Arrays/DuplicateArrayKeySniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Arrays/DuplicateArrayKeySniff.php new file mode 100644 index 0000000..7097fad --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Arrays/DuplicateArrayKeySniff.php @@ -0,0 +1,297 @@ +> + */ + private $keysSeenLt8 = []; + + /** + * Keep track of which array keys have been seen already on PHP >= 8.0. + * + * @since 1.0.0 + * + * @var array> + */ + private $keysSeenGt8 = []; + + /** + * Keep track of the maximum seen integer key to know what the next value will be for + * array items without a key on PHP < 8.0. + * + * @since 1.0.0 + * + * @var int + */ + private $currentMaxIntKeyLt8; + + /** + * Keep track of the maximum seen integer key to know what the next value will be for + * array items without a key on PHP >= 8.0. + * + * @since 1.0.0 + * + * @var int + */ + private $currentMaxIntKeyGt8; + + /** + * PHP version as configured or -1 if unknown. + * + * @since 1.0.0 + * + * @var int + */ + private $phpVersion; + + /** + * Process every part of the array declaration. + * + * This contains the default logic for the sniff, but can be overloaded in a concrete child class + * if needed. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the + * token was found. + * + * @return void + */ + public function processArray(File $phpcsFile) + { + // Reset properties before processing this array. + $this->keysSeenLt8 = []; + $this->keysSeenGt8 = []; + + if (isset($this->phpVersion) === false) { + // Set default value to prevent this code from running every time the sniff is triggered. + $this->phpVersion = -1; + + $phpVersion = Helper::getConfigData('php_version'); + if ($phpVersion !== null) { + $this->phpVersion = (int) $phpVersion; + } + } + + unset($this->currentMaxIntKeyLt8, $this->currentMaxIntKeyGt8); + + parent::processArray($phpcsFile); + } + + /** + * Process the tokens in an array key. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the + * token was found. + * @param int $startPtr The stack pointer to the first token in the "key" part of + * an array item. + * @param int $endPtr The stack pointer to the last token in the "key" part of + * an array item. + * @param int $itemNr Which item in the array is being handled. + * + * @return void + */ + public function processKey(File $phpcsFile, $startPtr, $endPtr, $itemNr) + { + $key = $this->getActualArrayKey($phpcsFile, $startPtr, $endPtr); + + if (isset($key) === false) { + // Key could not be determined. + return; + } + + $integerKey = \is_int($key); + + $errorMsg = 'Duplicate array key found. The value will be overwritten%s.' + . ' The %s array key "%s" was first seen on line %d'; + $errorCode = 'Found'; + $errors = []; + $baseData = [ + ($integerKey === true) ? 'integer' : 'string', + $key, + ]; + + /* + * Check if we've seen the key before. + */ + if (($this->phpVersion === -1 || $this->phpVersion < 80000) + && isset($this->keysSeenLt8[$key]) === true + ) { + $errors['phplt8'] = [ + 'data_subset' => $baseData, + 'error_suffix' => '', + 'code_suffix' => '', + ]; + + if ($integerKey === true) { + $errors['phplt8']['error_suffix'] = ' when using PHP < 8.0'; + $errors['phplt8']['code_suffix'] = 'ForPHPlt80'; + } + + $firstSeen = $this->keysSeenLt8[$key]; + $firstNonEmptyFirstSeen = $phpcsFile->findNext(Tokens::$emptyTokens, $firstSeen['ptr'], null, true); + + $errors['phplt8']['data_subset'][] = $this->tokens[$firstNonEmptyFirstSeen]['line']; + } + + if (($this->phpVersion === -1 || $this->phpVersion >= 80000) + && isset($this->keysSeenGt8[$key]) === true + ) { + $errors['phpgt8'] = [ + 'data_subset' => $baseData, + 'error_suffix' => '', + 'code_suffix' => '', + ]; + + if ($integerKey === true) { + $errors['phpgt8']['error_suffix'] = ' when using PHP >= 8.0'; + $errors['phpgt8']['code_suffix'] = 'ForPHPgte80'; + } + + $firstSeen = $this->keysSeenGt8[$key]; + $firstNonEmptyFirstSeen = $phpcsFile->findNext(Tokens::$emptyTokens, $firstSeen['ptr'], null, true); + + $errors['phpgt8']['data_subset'][] = $this->tokens[$firstNonEmptyFirstSeen]['line']; + } + + /* + * Throw the error(s). + * + * If no PHP version was passed, throw errors both for PHP < 8.0 and PHP >= 8.0. + * If a PHP version was set, only throw the error appropriate for the selected PHP version. + * If both errors would effectively be the same, only throw one. + */ + if ($errors !== []) { + $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true); + + if (isset($errors['phplt8'], $errors['phpgt8']) + && $errors['phplt8']['data_subset'] === $errors['phpgt8']['data_subset'] + ) { + // Only throw the error once if it would be the same for PHP < 8.0 and PHP >= 8.0. + $data = $errors['phplt8']['data_subset']; + \array_unshift($data, ''); + + $phpcsFile->addError($errorMsg, $firstNonEmpty, $errorCode, $data); + return; + } + + if (isset($errors['phplt8'])) { + $code = $errorCode . $errors['phplt8']['code_suffix']; + $data = $errors['phplt8']['data_subset']; + \array_unshift($data, $errors['phplt8']['error_suffix']); + + $phpcsFile->addError($errorMsg, $firstNonEmpty, $code, $data); + } + + if (isset($errors['phpgt8'])) { + $code = $errorCode . $errors['phpgt8']['code_suffix']; + $data = $errors['phpgt8']['data_subset']; + \array_unshift($data, $errors['phpgt8']['error_suffix']); + + $phpcsFile->addError($errorMsg, $firstNonEmpty, $code, $data); + } + + return; + } + + /* + * Key not seen before. Add to arrays. + */ + $this->keysSeenLt8[$key] = [ + 'item' => $itemNr, + 'ptr' => $startPtr, + ]; + $this->keysSeenGt8[$key] = [ + 'item' => $itemNr, + 'ptr' => $startPtr, + ]; + + if ($integerKey === true) { + if ((isset($this->currentMaxIntKeyLt8) === false && $key > -1) + || (isset($this->currentMaxIntKeyLt8) === true && $key > $this->currentMaxIntKeyLt8) + ) { + $this->currentMaxIntKeyLt8 = $key; + } + + if (isset($this->currentMaxIntKeyGt8) === false + || $key > $this->currentMaxIntKeyGt8 + ) { + $this->currentMaxIntKeyGt8 = $key; + } + } + } + + /** + * Process an array item without an array key. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the + * token was found. + * @param int $startPtr The stack pointer to the first token in the array item, + * which in this case will be the first token of the array + * value part of the array item. + * @param int $itemNr Which item in the array is being handled. + * + * @return void + */ + public function processNoKey(File $phpcsFile, $startPtr, $itemNr) + { + // Track the key for PHP < 8.0. + if (isset($this->currentMaxIntKeyLt8) === false) { + $this->currentMaxIntKeyLt8 = -1; + } + + ++$this->currentMaxIntKeyLt8; + $this->keysSeenLt8[$this->currentMaxIntKeyLt8] = [ + 'item' => $itemNr, + 'ptr' => $startPtr, + ]; + + // Track the key for PHP 8.0+. + if (isset($this->currentMaxIntKeyGt8) === false) { + $this->currentMaxIntKeyGt8 = -1; + } + + ++$this->currentMaxIntKeyGt8; + $this->keysSeenGt8[$this->currentMaxIntKeyGt8] = [ + 'item' => $itemNr, + 'ptr' => $startPtr, + ]; + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Arrays/MixedArrayKeyTypesSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Arrays/MixedArrayKeyTypesSniff.php new file mode 100644 index 0000000..49d9260 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Arrays/MixedArrayKeyTypesSniff.php @@ -0,0 +1,174 @@ +seenStringKey = false; + $this->seenNumericKey = false; + + parent::processArray($phpcsFile); + } + + /** + * Process the tokens in an array key. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the + * token was found. + * @param int $startPtr The stack pointer to the first token in the "key" part of + * an array item. + * @param int $endPtr The stack pointer to the last token in the "key" part of + * an array item. + * @param int $itemNr Which item in the array is being handled. + * + * @return true|void Returning `TRUE` will short-circuit the array item loop and stop processing. + * In effect, this means that the sniff will not examine the double arrow, the array + * value or comma for this array item and will not process any array items after this one. + */ + public function processKey(File $phpcsFile, $startPtr, $endPtr, $itemNr) + { + $key = $this->getActualArrayKey($phpcsFile, $startPtr, $endPtr); + if (isset($key) === false) { + // Key could not be determined. + return; + } + + $integerKey = \is_int($key); + + // Handle integer key. + if ($integerKey === true) { + if ($this->seenStringKey === false) { + if ($this->seenNumericKey !== false) { + // Already seen a numeric key before. + return; + } + + $this->seenNumericKey = true; + return; + } + + // Ok, so we've seen a string key before and now see an explicit numeric key. + $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true); + $phpcsFile->addError( + 'Arrays should have either numeric keys or string keys. Explicit numeric key detected,' + . ' while all previous keys in this array were string keys.', + $firstNonEmpty, + 'ExplicitNumericKey' + ); + + // Stop the loop. + return true; + } + + // Handle string key. + if ($this->seenNumericKey === false) { + if ($this->seenStringKey !== false) { + // Already seen a string key before. + return; + } + + $this->seenStringKey = true; + return; + } + + // Ok, so we've seen a numeric key before and now see a string key. + $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true); + $phpcsFile->addError( + 'Arrays should have either numeric keys or string keys. String key detected,' + . ' while all previous keys in this array were integer based keys.', + $firstNonEmpty, + 'StringKey' + ); + + // Stop the loop. + return true; + } + + /** + * Process an array item without an array key. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the + * token was found. + * @param int $startPtr The stack pointer to the first token in the array item, + * which in this case will be the first token of the array + * value part of the array item. + * @param int $itemNr Which item in the array is being handled. + * + * @return true|void Returning `TRUE` will short-circuit the array item loop and stop processing. + * In effect, this means that the sniff will not examine the array value or + * comma for this array item and will not process any array items after this one. + */ + public function processNoKey(File $phpcsFile, $startPtr, $itemNr) + { + if ($this->seenStringKey === false) { + if ($this->seenNumericKey !== false) { + // Already seen a numeric key before. + return; + } + + $this->seenNumericKey = true; + return; + } + + // Ok, so we've seen a string key before and now see an implicit numeric key. + $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true); + $phpcsFile->addError( + 'Arrays should have either numeric keys or string keys. Implicit numeric key detected,' + . ' while all previous keys in this array were string keys.', + $firstNonEmpty, + 'ImplicitNumericKey' + ); + + // Stop the loop. + return true; + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Arrays/MixedKeyedUnkeyedArraySniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Arrays/MixedKeyedUnkeyedArraySniff.php new file mode 100644 index 0000000..c47f217 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Arrays/MixedKeyedUnkeyedArraySniff.php @@ -0,0 +1,134 @@ + Key is the item number; value the stack point to the first non empty token in the item. + */ + private $itemsWithoutKey = []; + + /** + * Process the array declaration. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the + * token was found. + * + * @return void + */ + public function processArray(File $phpcsFile) + { + // Reset properties before processing this array. + $this->hasKeys = false; + $this->itemsWithoutKey = []; + + parent::processArray($phpcsFile); + } + + /** + * Process the tokens in an array key. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the + * token was found. + * @param int $startPtr The stack pointer to the first token in the "key" part of + * an array item. + * @param int $endPtr The stack pointer to the last token in the "key" part of + * an array item. + * @param int $itemNr Which item in the array is being handled. + * + * @return void + */ + public function processKey(File $phpcsFile, $startPtr, $endPtr, $itemNr) + { + $this->hasKeys = true; + + // Process any previously encountered items without keys. + if (empty($this->itemsWithoutKey) === false) { + foreach ($this->itemsWithoutKey as $itemNr => $stackPtr) { + $phpcsFile->addError( + 'Inconsistent array detected. A mix of keyed and unkeyed array items is not allowed.' + . ' The array item in position %d does not have an array key.', + $stackPtr, + 'Found', + [$itemNr] + ); + } + + // No need to do this again. + $this->itemsWithoutKey = []; + } + } + + /** + * Process an array item without an array key. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the + * token was found. + * @param int $startPtr The stack pointer to the first token in the array item, + * which in this case will be the first token of the array + * value part of the array item. + * @param int $itemNr Which item in the array is being handled. + * + * @return void + */ + public function processNoKey(File $phpcsFile, $startPtr, $itemNr) + { + $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true); + if ($firstNonEmpty === false || $this->tokens[$firstNonEmpty]['code'] === \T_COMMA) { + // Shouldn't really be possible, but this must be a parse error (empty array item). + return; + } + + // If we already know there are keys in the array, throw an error message straight away. + if ($this->hasKeys === true) { + $phpcsFile->addError( + 'Inconsistent array detected. A mix of keyed and unkeyed array items is not allowed.' + . ' The array item in position %d does not have an array key.', + $firstNonEmpty, + 'Found', + [$itemNr] + ); + } else { + // Save the array item info for later in case we do encounter an array key later on in the array. + $this->itemsWithoutKey[$itemNr] = $firstNonEmpty; + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/DisallowAnonClassParenthesesSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/DisallowAnonClassParenthesesSniff.php new file mode 100644 index 0000000..dae0125 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/DisallowAnonClassParenthesesSniff.php @@ -0,0 +1,112 @@ + + */ + public function register() + { + return [\T_ANON_CLASS]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + + // Note: no need to check for `false` as PHPCS won't retokenize `class` to `T_ANON_CLASS` in that case. + if ($tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) { + // No parentheses found. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no'); + return; + } + + if (isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false) { + /* + * Incomplete set of parentheses. Ignore. + * Shouldn't be possible as PHPCS won't retokenize `class` to `T_ANON_CLASS` in that case. + */ + // @codeCoverageIgnoreStart + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes'); + return; + // @codeCoverageIgnoreEnd + } + + $opener = $nextNonEmpty; + $closer = $tokens[$opener]['parenthesis_closer']; + $hasParams = $phpcsFile->findNext(Tokens::$emptyTokens, ($opener + 1), $closer, true); + if ($hasParams !== false) { + // There is something between the parentheses. Ignore. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes, with parameter(s)'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes'); + + $fix = $phpcsFile->addFixableError( + 'Parenthesis not allowed when creating a new anonymous class without passing parameters', + $stackPtr, + 'Found' + ); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + for ($i = $opener; $i <= $closer; $i++) { + if (isset(Tokens::$commentTokens[$tokens[$i]['code']]) === true) { + continue; + } + + $phpcsFile->fixer->replaceToken($i, ''); + } + + $phpcsFile->fixer->endChangeset(); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/DisallowFinalClassSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/DisallowFinalClassSniff.php new file mode 100644 index 0000000..4e3f471 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/DisallowFinalClassSniff.php @@ -0,0 +1,116 @@ + + */ + public function register() + { + return [\T_CLASS]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $classProp = ObjectDeclarations::getClassProperties($phpcsFile, $stackPtr); + if ($classProp['is_final'] === false) { + if ($classProp['is_abstract'] === true) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'abstract'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'not abstract, not final'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'final'); + + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($nextNonEmpty === false) { + // Live coding or parse error. + return; + } + + // No extra safeguards needed, we know the keyword will exist based on the check above. + $finalKeyword = $phpcsFile->findPrevious(\T_FINAL, ($stackPtr - 1)); + $snippetEnd = $nextNonEmpty; + $classCloser = ''; + + $tokens = $phpcsFile->getTokens(); + if (isset($tokens[$stackPtr]['scope_opener']) === true) { + $snippetEnd = $tokens[$stackPtr]['scope_opener']; + $classCloser = '}'; + } + + $snippet = GetTokensAsString::compact($phpcsFile, $finalKeyword, $snippetEnd, true); + $fix = $phpcsFile->addFixableError( + 'Declaring a class as final is not allowed. Found: %s%s', + $finalKeyword, + 'FinalClassFound', + [$snippet, $classCloser] + ); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->replaceToken($finalKeyword, ''); + + // Remove redundant whitespace. + for ($i = ($finalKeyword + 1); $i < $stackPtr; $i++) { + if ($tokens[$i]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken($i, ''); + continue; + } + + break; + } + + $phpcsFile->fixer->endChangeset(); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/ModifierKeywordOrderSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/ModifierKeywordOrderSniff.php new file mode 100644 index 0000000..88fdfe2 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/ModifierKeywordOrderSniff.php @@ -0,0 +1,188 @@ + + */ + public function register() + { + return [\T_CLASS]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $classProp = ObjectDeclarations::getClassProperties($phpcsFile, $stackPtr); + + if ($classProp['readonly_token'] === false + || ($classProp['final_token'] === false && $classProp['abstract_token'] === false) + ) { + /* + * Either no modifier keywords found at all; or only one type of modifier + * keyword (abstract/final or readonly) declared, but not both. No ordering needed. + */ + return; + } + + if ($classProp['final_token'] !== false && $classProp['abstract_token'] !== false) { + // Parse error. Ignore. + return; + } + + $readonly = $classProp['readonly_token']; + + if ($classProp['final_token'] !== false) { + $extendability = $classProp['final_token']; + } else { + $extendability = $classProp['abstract_token']; + } + + if ($readonly < $extendability) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, self::READONLY_EXTEND); + } else { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, self::EXTEND_READONLY); + } + + $message = 'Class modifier keywords are not in the correct order. Expected: "%s", found: "%s"'; + + switch ($this->order) { + case self::READONLY_EXTEND: + if ($readonly < $extendability) { + // Order is correct. Nothing to do. + return; + } + + $this->handleError($phpcsFile, $extendability, $readonly); + break; + + case self::EXTEND_READONLY: + default: + if ($extendability < $readonly) { + // Order is correct. Nothing to do. + return; + } + + $this->handleError($phpcsFile, $readonly, $extendability); + break; + } + } + + /** + * Throw the error and potentially fix it. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $firstKeyword The position of the first keyword found. + * @param int $secondKeyword The position of the second keyword token. + * + * @return void + */ + private function handleError(File $phpcsFile, $firstKeyword, $secondKeyword) + { + $tokens = $phpcsFile->getTokens(); + + $message = 'Class modifier keywords are not in the correct order. Expected: "%s", found: "%s"'; + $data = [ + $tokens[$secondKeyword]['content'] . ' ' . $tokens[$firstKeyword]['content'], + $tokens[$firstKeyword]['content'] . ' ' . $tokens[$secondKeyword]['content'], + ]; + + $fix = $phpcsFile->addFixableError($message, $firstKeyword, 'Incorrect', $data); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + $phpcsFile->fixer->replaceToken($secondKeyword, ''); + + // Prevent leaving behind trailing whitespace. + $i = ($secondKeyword + 1); + while ($tokens[$i]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken($i, ''); + ++$i; + } + + // Use the original token content as the case used for keywords is not the concern of this sniff. + $phpcsFile->fixer->addContentBefore($firstKeyword, $tokens[$secondKeyword]['content'] . ' '); + + $phpcsFile->fixer->endChangeset(); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/RequireAnonClassParenthesesSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/RequireAnonClassParenthesesSniff.php new file mode 100644 index 0000000..2715b39 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/RequireAnonClassParenthesesSniff.php @@ -0,0 +1,81 @@ + + */ + public function register() + { + return [\T_ANON_CLASS]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + + // Note: no need to check for `false` as PHPCS won't retokenize `class` to `T_ANON_CLASS` in that case. + if ($tokens[$nextNonEmpty]['code'] === \T_OPEN_PARENTHESIS) { + // Parentheses found. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no'); + + $fix = $phpcsFile->addFixableError( + 'Parenthesis required when creating a new anonymous class.', + $stackPtr, + 'Missing' + ); + + if ($fix === true) { + $phpcsFile->fixer->addContent($stackPtr, '()'); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/RequireFinalClassSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/RequireFinalClassSniff.php new file mode 100644 index 0000000..843f1ad --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Classes/RequireFinalClassSniff.php @@ -0,0 +1,102 @@ + + */ + public function register() + { + return [\T_CLASS]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $classProp = ObjectDeclarations::getClassProperties($phpcsFile, $stackPtr); + if ($classProp['is_final'] === true) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'final'); + return; + } + + if ($classProp['is_abstract'] === true) { + // Abstract classes can't be final. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'abstract'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'not abstract, not final'); + + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($nextNonEmpty === false) { + // Live coding or parse error. + return; + } + + $snippetEnd = $nextNonEmpty; + $classCloser = ''; + + $tokens = $phpcsFile->getTokens(); + if (isset($tokens[$stackPtr]['scope_opener']) === true) { + $snippetEnd = $tokens[$stackPtr]['scope_opener']; + $classCloser = '}'; + } + + $snippet = GetTokensAsString::compact($phpcsFile, $stackPtr, $snippetEnd, true); + $fix = $phpcsFile->addFixableError( + 'A non-abstract class should be declared as final. Found: %s%s', + $stackPtr, + 'NonFinalClassFound', + [$snippet, $classCloser] + ); + + if ($fix === true) { + $phpcsFile->fixer->addContentBefore($stackPtr, 'final '); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/ConstructorDestructorReturnSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/ConstructorDestructorReturnSniff.php new file mode 100644 index 0000000..6f78cb6 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/ConstructorDestructorReturnSniff.php @@ -0,0 +1,211 @@ + + */ + public function register() + { + return [\T_FUNCTION]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if (isset($this->phpVersion) === false || \defined('PHP_CODESNIFFER_IN_TESTS')) { + // Set default value to prevent this code from running every time the sniff is triggered. + $this->phpVersion = 0; + + $phpVersion = Helper::getConfigData('php_version'); + if ($phpVersion !== null) { + $this->phpVersion = (int) $phpVersion; + } + } + + $scopePtr = Scopes::validDirectScope($phpcsFile, $stackPtr, Tokens::$ooScopeTokens); + if ($scopePtr === false) { + // Not an OO method. + return; + } + + $functionName = FunctionDeclarations::getName($phpcsFile, $stackPtr); + $functionNameLC = \strtolower($functionName); + + if ($functionNameLC === '__construct' || $functionNameLC === '__destruct') { + $functionType = \sprintf('A "%s()" magic method', $functionNameLC); + } else { + // If the PHP version is explicitly set to PHP 8.0 or higher, ignore PHP 4-style constructors. + if ($this->phpVersion >= 80000) { + return; + } + + // This may be a PHP 4-style constructor which should be handled. + $OOName = ObjectDeclarations::getName($phpcsFile, $scopePtr); + + if (empty($OOName) === true) { + // Anonymous class or parse error. The function can't be a PHP 4-style constructor. + return; + } + + if (NamingConventions::isEqual($functionName, $OOName) === false) { + // Class and function name not the same, so not a PHP 4-style constructor. + return; + } + + if (Namespaces::determineNamespace($phpcsFile, $stackPtr) !== '') { + /* + * Namespaced methods with the same name as the class are treated as + * regular methods, so we can bow out if we're in a namespace. + * + * Note: the exception to this is PHP 5.3.0-5.3.2. This is currently + * not dealt with. + */ + return; + } + + $functionType = 'A PHP 4-style constructor'; + } + + /* + * OK, so now we know for sure that this is a constructor/destructor method. + */ + + // Check for a return type. + $tokens = $phpcsFile->getTokens(); + $properties = FunctionDeclarations::getProperties($phpcsFile, $stackPtr); + if ($properties['return_type'] !== '' && $properties['return_type_token'] !== false) { + $data = [ + $functionType, + $properties['return_type'], + ]; + + $fix = $phpcsFile->addFixableError( + '%s can not declare a return type. Found: %s', + $properties['return_type_token'], + 'ReturnTypeFound', + $data + ); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + $parensCloser = $tokens[$stackPtr]['parenthesis_closer']; + for ($i = ($parensCloser + 1); $i <= $properties['return_type_end_token']; $i++) { + if (isset(Tokens::$commentTokens[$tokens[$i]['code']])) { + // Ignore comments and leave them be. + continue; + } + + $phpcsFile->fixer->replaceToken($i, ''); + } + + $phpcsFile->fixer->endChangeset(); + } + } + + if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) { + // Abstract/interface method, live coding or parse error. + return; + } + + // Check for a value being returned. + $current = $tokens[$stackPtr]['scope_opener']; + $end = $tokens[$stackPtr]['scope_closer']; + + // Not searching for arrow functions as those have an implicit return, so no + $search = Collections::functionDeclarationTokens(); + $search[\T_RETURN] = \T_RETURN; + + do { + $current = $phpcsFile->findNext($search, ($current + 1), $end); + if ($current === false) { + break; + } + + if (isset(Collections::functionDeclarationTokens()[$tokens[$current]['code']]) + && isset($tokens[$current]['scope_closer']) + ) { + // Skip over nested function/closure declarations. + $current = $tokens[$current]['scope_closer']; + continue; + } + + $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), $end, true); + if ($next === false + || $tokens[$next]['code'] === \T_SEMICOLON + || $tokens[$next]['code'] === \T_CLOSE_TAG + ) { + // Return statement without value. + continue; + } + + $endOfStatement = BCFile::findEndOfStatement($phpcsFile, $next); + + $data = [ + $functionType, + GetTokensAsString::compact($phpcsFile, $current, $endOfStatement, true), + ]; + + $phpcsFile->addWarning( + '%s can not return a value. Found: "%s"', + $current, + 'ReturnValueFound', + $data + ); + } while ($current < $end); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/ForeachUniqueAssignmentSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/ForeachUniqueAssignmentSniff.php new file mode 100644 index 0000000..8895c56 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/ForeachUniqueAssignmentSniff.php @@ -0,0 +1,153 @@ + + */ + public function register() + { + return [\T_FOREACH]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) { + // Parse error or live coding, not our concern. + return; + } + + $opener = $tokens[$stackPtr]['parenthesis_opener']; + $closer = $tokens[$stackPtr]['parenthesis_closer']; + + $asPtr = $phpcsFile->findNext(\T_AS, ($opener + 1), $closer); + if ($asPtr === false) { + // Parse error or live coding, not our concern. + return; + } + + // Real target. + $find = [\T_DOUBLE_ARROW]; + // Prevent matching on double arrows within a list assignment. + $find += Collections::listTokens(); + + $doubleArrowPtr = $phpcsFile->findNext($find, ($asPtr + 1), $closer); + if ($doubleArrowPtr === false + || $tokens[$doubleArrowPtr]['code'] !== \T_DOUBLE_ARROW + ) { + // No key assignment. + return; + } + + $isListAssignment = $phpcsFile->findNext(Tokens::$emptyTokens, ($doubleArrowPtr + 1), $closer, true); + if ($isListAssignment === false) { + // Parse error or live coding, not our concern. + } + + $keyAsString = \ltrim(GetTokensAsString::noEmpties($phpcsFile, ($asPtr + 1), ($doubleArrowPtr - 1)), '&'); + $valueAssignments = []; + if (isset(Collections::listTokens()[$tokens[$isListAssignment]['code']]) === false) { + // Single value assignment. + $valueAssignments[] = GetTokensAsString::noEmpties($phpcsFile, ($doubleArrowPtr + 1), ($closer - 1)); + } else { + // List assignment. + $assignments = Lists::getAssignments($phpcsFile, $isListAssignment); + foreach ($assignments as $listItem) { + if ($listItem['assignment'] === '') { + // Ignore empty list assignments. + continue; + } + + // Note: this doesn't take nested lists into account (yet). + $valueAssignments[] = $listItem['assignment']; + } + } + + if (empty($valueAssignments)) { + // No assignments found. + return; + } + + foreach ($valueAssignments as $valueAsString) { + $valueAsString = \ltrim($valueAsString, '&'); + + if ($keyAsString !== $valueAsString) { + // Key and value not the same. + continue; + } + + $error = 'The variables used for the key and the value in a foreach assignment should be unique.'; + $error .= 'Both the key and the value will currently be assigned to: "%s"'; + + $fix = $phpcsFile->addFixableError($error, $doubleArrowPtr, 'NotUnique', [$valueAsString]); + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + // Remove the key. + for ($i = ($asPtr + 1); $i < ($doubleArrowPtr + 1); $i++) { + if ($tokens[$i]['code'] === \T_WHITESPACE + && isset(Tokens::$commentTokens[$tokens[($i + 1)]['code']]) + ) { + // Don't remove whitespace when followed directly by a comment. + continue; + } + + if (isset(Tokens::$commentTokens[$tokens[$i]['code']])) { + // Don't remove comments. + continue; + } + + // Remove everything else. + $phpcsFile->fixer->replaceToken($i, ''); + } + + $phpcsFile->fixer->endChangeset(); + } + + break; + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/NoDoubleNegativeSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/NoDoubleNegativeSniff.php new file mode 100644 index 0000000..549c8cd --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/NoDoubleNegativeSniff.php @@ -0,0 +1,269 @@ + + */ + private $operatorsWithLowerPrecedence; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.2.0 + * + * @return array + */ + public function register() + { + // Collect all the operators only once. + $this->operatorsWithLowerPrecedence = Tokens::$assignmentTokens; + $this->operatorsWithLowerPrecedence += Tokens::$booleanOperators; + $this->operatorsWithLowerPrecedence += Tokens::$comparisonTokens; + $this->operatorsWithLowerPrecedence += Tokens::$operators; + $this->operatorsWithLowerPrecedence[\T_INLINE_THEN] = \T_INLINE_THEN; + $this->operatorsWithLowerPrecedence[\T_INLINE_ELSE] = \T_INLINE_ELSE; + + return [\T_BOOLEAN_NOT]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.2.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $notCount = 1; + $lastNot = $stackPtr; + for ($afterNot = ($stackPtr + 1); $afterNot < $phpcsFile->numTokens; $afterNot++) { + if (isset(Tokens::$emptyTokens[$tokens[$afterNot]['code']])) { + continue; + } + + if ($tokens[$afterNot]['code'] === \T_BOOLEAN_NOT) { + $lastNot = $afterNot; + ++$notCount; + continue; + } + + break; + } + + if ($notCount === 1) { + // Singular unary not-operator. Nothing to do. + return; + } + + $found = \trim(GetTokensAsString::compact($phpcsFile, $stackPtr, $lastNot)); + $data = [$found]; + + if (($notCount % 2) === 1) { + /* + * Oh dear... silly code time, found a triple negative (or other uneven number), + * this should just be a singular not-operator. + */ + $fix = $phpcsFile->addFixableError( + 'Triple negative (or more) detected. Use a singular not (!) operator instead. Found: %s', + $stackPtr, + 'FoundTriple', + $data + ); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + $this->removeNotAndTrailingSpaces($phpcsFile, $stackPtr, $lastNot); + + $phpcsFile->fixer->endChangeset(); + } + + // Only throw one error, even if there are more than two not-operators. + return $lastNot; + } + + /* + * Found a double negative, which should be a boolean cast. + */ + + $fixable = true; + + /* + * If whatever is being "cast" is within parentheses, we're good. + * If not, we need to prevent creating a change in behaviour + * when what follows is an `$x instanceof ...` expression, as + * the "instanceof" operator is right between a boolean cast + * and the ! operator precedence-wise. + * + * Note: this only applies to double negative, not triple negative. + * + * @link https://www.php.net/language.operators.precedence + */ + if ($tokens[$afterNot]['code'] !== \T_OPEN_PARENTHESIS) { + $end = Parentheses::getLastCloser($phpcsFile, $stackPtr); + if ($end === false) { + $end = BCFile::findEndOfStatement($phpcsFile, $stackPtr); + } + + for ($nextRelevant = $afterNot; $nextRelevant < $end; $nextRelevant++) { + if (isset(Tokens::$emptyTokens[$tokens[$nextRelevant]['code']])) { + continue; + } + + if ($tokens[$nextRelevant]['code'] === \T_INSTANCEOF) { + $fixable = false; + break; + } + + if (isset($this->operatorsWithLowerPrecedence[$tokens[$nextRelevant]['code']])) { + // The expression the `!` belongs to has ended. + break; + } + + // Skip over anything within some form of brackets. + if (isset($tokens[$nextRelevant]['scope_closer']) + && ($nextRelevant === $tokens[$nextRelevant]['scope_opener'] + || $nextRelevant === $tokens[$nextRelevant]['scope_condition']) + ) { + $nextRelevant = $tokens[$nextRelevant]['scope_closer']; + continue; + } + + if (isset($tokens[$nextRelevant]['bracket_opener'], $tokens[$nextRelevant]['bracket_closer']) + && $nextRelevant === $tokens[$nextRelevant]['bracket_opener'] + ) { + $nextRelevant = $tokens[$nextRelevant]['bracket_closer']; + continue; + } + + if ($tokens[$nextRelevant]['code'] === \T_OPEN_PARENTHESIS + && isset($tokens[$nextRelevant]['parenthesis_closer']) + ) { + $nextRelevant = $tokens[$nextRelevant]['parenthesis_closer']; + continue; + } + + // Skip over attributes (just in case). + if ($tokens[$nextRelevant]['code'] === \T_ATTRIBUTE + && isset($tokens[$nextRelevant]['attribute_closer']) + ) { + $nextRelevant = $tokens[$nextRelevant]['attribute_closer']; + continue; + } + } + } + + $error = 'Double negative detected. Use a (bool) cast %s instead. Found: %s'; + $code = 'FoundDouble'; + $data = [ + '', + $found, + ]; + + if ($fixable === false) { + $code = 'FoundDoubleWithInstanceof'; + $data[0] = 'and parentheses around the instanceof expression'; + + // Don't auto-fix in combination with instanceof. + $phpcsFile->addError($error, $stackPtr, $code, $data); + + // Only throw one error, even if there are more than two not-operators. + return $lastNot; + } + + $fix = $phpcsFile->addFixableError($error, $stackPtr, $code, $data); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + $this->removeNotAndTrailingSpaces($phpcsFile, $stackPtr, $lastNot); + + $phpcsFile->fixer->replaceToken($lastNot, '(bool)'); + + $phpcsFile->fixer->endChangeset(); + } + + // Only throw one error, even if there are more than two not-operators. + return $lastNot; + } + + /** + * Remove boolean not-operators and trailing whitespace after those, + * but don't remove comments or trailing whitespace after comments. + * + * @since 1.2.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $lastNot The position of the last boolean not token + * in the chain. + * + * @return void + */ + private function removeNotAndTrailingSpaces(File $phpcsFile, $stackPtr, $lastNot) + { + $tokens = $phpcsFile->getTokens(); + $ignore = false; + + for ($i = $stackPtr; $i < $lastNot; $i++) { + if (isset(Tokens::$commentTokens[$tokens[$i]['code']])) { + // Ignore comments and whitespace after comments. + $ignore = true; + continue; + } + + if ($tokens[$i]['code'] === \T_WHITESPACE && $ignore === false) { + $phpcsFile->fixer->replaceToken($i, ''); + continue; + } + + if ($tokens[$i]['code'] === \T_BOOLEAN_NOT) { + $ignore = false; + $phpcsFile->fixer->replaceToken($i, ''); + } + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/NoEchoSprintfSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/NoEchoSprintfSniff.php new file mode 100644 index 0000000..67035f2 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/NoEchoSprintfSniff.php @@ -0,0 +1,131 @@ + + */ + private $targetFunctions = [ + 'sprintf' => 'printf', + 'vsprintf' => 'vprintf', + ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.1.0 + * + * @return array + */ + public function register() + { + return [\T_ECHO]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.1.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $skip = Tokens::$emptyTokens; + $skip[] = \T_NS_SEPARATOR; + + $next = $phpcsFile->findNext($skip, ($stackPtr + 1), null, true); + if ($next === false + || $tokens[$next]['code'] !== \T_STRING + || isset($this->targetFunctions[\strtolower($tokens[$next]['content'])]) === false + ) { + // Not our target. + return; + } + + $detectedFunction = \strtolower($tokens[$next]['content']); + + $openParens = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true); + if ($openParens === false + || $tokens[$openParens]['code'] !== \T_OPEN_PARENTHESIS + || isset($tokens[$openParens]['parenthesis_closer']) === false + ) { + // Live coding/parse error. + return; + } + + $closeParens = $tokens[$openParens]['parenthesis_closer']; + $afterFunctionCall = $phpcsFile->findNext(Tokens::$emptyTokens, ($closeParens + 1), null, true); + if ($afterFunctionCall === false + || ($tokens[$afterFunctionCall]['code'] !== \T_SEMICOLON + && $tokens[$afterFunctionCall]['code'] !== \T_CLOSE_TAG) + ) { + // Live coding/parse error or compound echo statement. + return; + } + + $fix = $phpcsFile->addFixableError( + 'Unnecessary "echo %s(...)" found. Use "%s(...)" instead.', + $next, + 'Found', + [ + $tokens[$next]['content'], + $this->targetFunctions[$detectedFunction], + ] + ); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + // Remove echo and whitespace. + $phpcsFile->fixer->replaceToken($stackPtr, ''); + + for ($i = ($stackPtr + 1); $i < $next; $i++) { + if ($tokens[$i]['code'] !== \T_WHITESPACE) { + break; + } + + $phpcsFile->fixer->replaceToken($i, ''); + } + + $phpcsFile->fixer->replaceToken($next, $this->targetFunctions[$detectedFunction]); + + $phpcsFile->fixer->endChangeset(); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/StaticInFinalClassSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/StaticInFinalClassSniff.php new file mode 100644 index 0000000..2136bcd --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/CodeAnalysis/StaticInFinalClassSniff.php @@ -0,0 +1,216 @@ + + */ + private $validOOScopes = [ + \T_CLASS, // Only if final. + \T_ANON_CLASS, // Final by nature. + \T_ENUM, // Final by design. + ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.0.0 + * + * @return array + */ + public function register() + { + return [ + // These tokens are used to retrieve return types reliably. + \T_FUNCTION, + \T_FN, + // While this is our "real" target. + \T_STATIC, + // But we also need this as after "instanceof", `static` is tokenized as `T_STRING in PHPCS < 4.0.0. + // See: https://github.com/squizlabs/PHP_CodeSniffer/pull/3121 + \T_STRING, + ]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + if ($tokens[$stackPtr]['code'] === \T_STRING + && \strtolower($tokens[$stackPtr]['content']) !== 'static' + ) { + return; + } + + if ($tokens[$stackPtr]['code'] === \T_FUNCTION + || $tokens[$stackPtr]['code'] === \T_FN + ) { + /* + * Check return types for methods in final classes, anon classes and enums. + * + * Will return the scope opener of the function to prevent potential duplicate notifications. + */ + $scopeOpener = $stackPtr; + if (isset($tokens[$stackPtr]['scope_opener']) === true) { + $scopeOpener = $tokens[$stackPtr]['scope_opener']; + } + + if ($tokens[$stackPtr]['code'] === \T_FUNCTION) { + $ooPtr = Scopes::validDirectScope($phpcsFile, $stackPtr, $this->validOOScopes); + if ($ooPtr === false) { + // Method in a trait (not known where it is used), interface (never final) or not in an OO scope. + return $scopeOpener; + } + } else { + $ooPtr = Conditions::getLastCondition($phpcsFile, $stackPtr, $this->validOOScopes); + if ($ooPtr === false) { + // Arrow function outside of OO. + return $scopeOpener; + } + } + + if ($tokens[$ooPtr]['code'] === \T_CLASS) { + $classProps = ObjectDeclarations::getClassProperties($phpcsFile, $ooPtr); + if ($classProps['is_final'] === false) { + // Method in a non-final class. + return $scopeOpener; + } + } + + $functionProps = FunctionDeclarations::getProperties($phpcsFile, $stackPtr); + if ($functionProps['return_type'] === '') { + return $scopeOpener; + } + + $staticPtr = $phpcsFile->findNext( + \T_STATIC, + $functionProps['return_type_token'], + ($functionProps['return_type_end_token'] + 1) + ); + + if ($staticPtr === false) { + return $scopeOpener; + } + + // Found a return type containing the `static` type. + $this->handleError($phpcsFile, $staticPtr, 'ReturnType', '"static" return type'); + + return $scopeOpener; + } + + /* + * Check other uses of static. + */ + $functionPtr = Conditions::getLastCondition($phpcsFile, $stackPtr, [\T_FUNCTION, \T_CLOSURE]); + if ($functionPtr === false || $tokens[$functionPtr]['code'] === \T_CLOSURE) { + /* + * When `false`, this code is absolutely invalid, but not something to be addressed via this sniff. + * When a closure, we're not interested in it. The closure class is final, but closures + * can be bound to other classes. This needs further research and should maybe get its own sniff. + */ + return; + } + + $ooPtr = Scopes::validDirectScope($phpcsFile, $functionPtr, $this->validOOScopes); + if ($ooPtr === false) { + // Not in an OO context. + return; + } + + if ($tokens[$ooPtr]['code'] === \T_CLASS) { + $classProps = ObjectDeclarations::getClassProperties($phpcsFile, $ooPtr); + if ($classProps['is_final'] === false) { + // Token in a non-final class. + return; + } + } + + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + if ($prevNonEmpty !== false) { + if ($tokens[$prevNonEmpty]['code'] === \T_INSTANCEOF) { + $prevPrevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevNonEmpty - 1), null, true); + $extraMsg = GetTokensAsString::compact($phpcsFile, $prevPrevNonEmpty, $stackPtr, true); + $this->handleError($phpcsFile, $stackPtr, 'InstanceOf', '"' . $extraMsg . '"'); + return; + } + + if ($tokens[$prevNonEmpty]['code'] === \T_NEW) { + $this->handleError($phpcsFile, $stackPtr, 'NewInstance', '"new static"'); + return; + } + } + + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['code'] === \T_DOUBLE_COLON) { + $nextNextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true); + $extraMsg = GetTokensAsString::compact($phpcsFile, $stackPtr, $nextNextNonEmpty, true); + $this->handleError($phpcsFile, $stackPtr, 'ScopeResolution', '"' . $extraMsg . '"'); + return; + } + } + + /** + * Throw and potentially fix the error. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of erroneous `T_STATIC` token. + * @param string $errorCode The error code for the message. + * @param string $extraMsg Addition to the error message. + * + * @return void + */ + private function handleError($phpcsFile, $stackPtr, $errorCode, $extraMsg) + { + $fix = $phpcsFile->addFixableError( + 'Use "self" instead of "static" when using late static binding in a final OO construct. Found: %s', + $stackPtr, + $errorCode, + [$extraMsg] + ); + + if ($fix === true) { + $phpcsFile->fixer->replaceToken($stackPtr, 'self'); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Constants/LowercaseClassResolutionKeywordSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Constants/LowercaseClassResolutionKeywordSniff.php new file mode 100644 index 0000000..7a67e47 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Constants/LowercaseClassResolutionKeywordSniff.php @@ -0,0 +1,106 @@ + + */ + public function register() + { + return [\T_STRING]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $content = $tokens[$stackPtr]['content']; + $contentLC = \strtolower($content); + + if ($contentLC !== 'class') { + return; + } + + $nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($nextToken !== false && $tokens[$nextToken]['code'] === \T_OPEN_PARENTHESIS) { + // Function call or declaration for a function called "class". + return; + } + + $prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + if ($prevToken === false || $tokens[$prevToken]['code'] !== \T_DOUBLE_COLON) { + return; + } + + if ($contentLC === $content) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'lowercase'); + return; + } + + $error = "The ::class keyword for class name resolution must be in lowercase. Expected: '::%s'; found: '::%s'"; + $data = [ + $contentLC, + $content, + ]; + + $errorCode = ''; + if (\strtoupper($content) === $content) { + $errorCode = 'Uppercase'; + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'uppercase'); + } else { + $errorCode = 'Mixedcase'; + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'mixed case'); + } + + $fix = $phpcsFile->addFixableError($error, $stackPtr, $errorCode, $data); + if ($fix === true) { + $phpcsFile->fixer->replaceToken($stackPtr, $contentLC); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Constants/ModifierKeywordOrderSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Constants/ModifierKeywordOrderSniff.php new file mode 100644 index 0000000..4b2e718 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Constants/ModifierKeywordOrderSniff.php @@ -0,0 +1,199 @@ + + */ + public function register() + { + return [\T_CONST]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if (Scopes::isOOConstant($phpcsFile, $stackPtr) === false) { + return; + } + + $tokens = $phpcsFile->getTokens(); + $valid = Collections::constantModifierKeywords() + Tokens::$emptyTokens; + + $finalPtr = false; + $visibilityPtr = false; + + for ($i = ($stackPtr - 1); $i > 0; $i--) { + if (isset($valid[$tokens[$i]['code']]) === false) { + break; + } + + if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) { + continue; + } + + if ($tokens[$i]['code'] === \T_FINAL) { + $finalPtr = $i; + } else { + $visibilityPtr = $i; + } + } + + if ($finalPtr === false || $visibilityPtr === false) { + /* + * Either no modifier keywords found at all; or only one type of modifier + * keyword (final or visibility) declared, but not both. No ordering needed. + */ + return; + } + + if ($visibilityPtr < $finalPtr) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, self::VISIBILITY_FINAL); + } else { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, self::FINAL_VISIBILITY); + } + + $message = 'OO constant modifier keywords are not in the correct order. Expected: "%s", found: "%s"'; + + switch ($this->order) { + case self::VISIBILITY_FINAL: + if ($visibilityPtr < $finalPtr) { + // Order is correct. Nothing to do. + return; + } + + $this->handleError($phpcsFile, $finalPtr, $visibilityPtr); + break; + + case self::FINAL_VISIBILITY: + default: + if ($finalPtr < $visibilityPtr) { + // Order is correct. Nothing to do. + return; + } + + $this->handleError($phpcsFile, $visibilityPtr, $finalPtr); + break; + } + } + + /** + * Throw the error and potentially fix it. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $firstKeyword The position of the first keyword found. + * @param int $secondKeyword The position of the second keyword token. + * + * @return void + */ + private function handleError(File $phpcsFile, $firstKeyword, $secondKeyword) + { + $tokens = $phpcsFile->getTokens(); + + $message = 'Constant modifier keywords are not in the correct order. Expected: "%s", found: "%s"'; + $data = [ + $tokens[$secondKeyword]['content'] . ' ' . $tokens[$firstKeyword]['content'], + $tokens[$firstKeyword]['content'] . ' ' . $tokens[$secondKeyword]['content'], + ]; + + $fix = $phpcsFile->addFixableError($message, $firstKeyword, 'Incorrect', $data); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + $phpcsFile->fixer->replaceToken($secondKeyword, ''); + + // Prevent leaving behind trailing whitespace. + $i = ($secondKeyword + 1); + while ($tokens[$i]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken($i, ''); + ++$i; + } + + // Use the original token content as the case used for keywords is not the concern of this sniff. + $phpcsFile->fixer->addContentBefore($firstKeyword, $tokens[$secondKeyword]['content'] . ' '); + + $phpcsFile->fixer->endChangeset(); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Constants/UppercaseMagicConstantsSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Constants/UppercaseMagicConstantsSniff.php new file mode 100644 index 0000000..348cf65 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Constants/UppercaseMagicConstantsSniff.php @@ -0,0 +1,89 @@ + + */ + public function register() + { + return Tokens::$magicConstants; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $content = $tokens[$stackPtr]['content']; + $contentUC = \strtoupper($content); + if ($contentUC === $content) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'uppercase'); + return; + } + + $error = 'Magic constants should be in uppercase. Expected: %s; found: %s'; + $errorCode = ''; + $data = [ + $contentUC, + $content, + ]; + + if (\strtolower($content) === $content) { + $errorCode = 'Lowercase'; + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'lowercase'); + } else { + $errorCode = 'Mixedcase'; + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'mixed case'); + } + + $fix = $phpcsFile->addFixableError($error, $stackPtr, $errorCode, $data); + if ($fix === true) { + $phpcsFile->fixer->replaceToken($stackPtr, $contentUC); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/ControlStructures/DisallowAlternativeSyntaxSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/ControlStructures/DisallowAlternativeSyntaxSniff.php new file mode 100644 index 0000000..ab78a70 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/ControlStructures/DisallowAlternativeSyntaxSniff.php @@ -0,0 +1,216 @@ + + */ + public function register() + { + $targets = Collections::alternativeControlStructureSyntaxes(); + + // Don't look for elseif/else as they need to be dealt with in one go with the if. + unset($targets[\T_ELSEIF], $targets[\T_ELSE]); + + return $targets; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + /* + * Ignore control structures without body (i.e. single line control structures). + * This doesn't ignore _empty_ bodies. + */ + if (ControlStructures::hasBody($phpcsFile, $stackPtr, true) === false) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'single line (without body)'); + return; + } + + $tokens = $phpcsFile->getTokens(); + + /* + * Check if the control structure uses alternative syntax. + */ + if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) { + // No scope opener found: inline control structure or parse error. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'inline'); + return; + } + + $opener = $tokens[$stackPtr]['scope_opener']; + $closer = $tokens[$stackPtr]['scope_closer']; + + if ($tokens[$opener]['code'] !== \T_COLON) { + // Curly brace syntax (not our concern). + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'curly braces'); + return; + } + + /* + * As of here, we *know* the control structure must be using alternative syntax and + * must have all scope openers/closers set as, in case of parse errors, PHPCS wouldn't + * have set the scope opener, even for the first `if`. + * + * Also note that alternative syntax cannot be used with `else if`, so we don't need to take that + * into account. + */ + + /* + * Determine whether there is inline HTML. + * + * For "chained" control structures (if - elseif - else), the complete control structure + * needs to be examined in one go as these cannot be changed individually, only as a complete group. + */ + $closedScopes = Collections::closedScopes(); + $find = $closedScopes; + $find[\T_INLINE_HTML] = \T_INLINE_HTML; + + $chainedIssues = []; + $hasInlineHTML = false; + $currentPtr = $stackPtr; + + do { + $opener = $tokens[$currentPtr]['scope_opener']; + $closer = $tokens[$currentPtr]['scope_closer']; + $chainedIssues[$opener] = $closer; + + if ($hasInlineHTML === true) { + // No need to search the contents, we already know there is inline HTML. + $currentPtr = $closer; + continue; + } + + $inlineHTMLPtr = $opener; + + do { + $inlineHTMLPtr = $phpcsFile->findNext($find, ($inlineHTMLPtr + 1), $closer); + if ($tokens[$inlineHTMLPtr]['code'] === \T_INLINE_HTML) { + $hasInlineHTML = true; + break; + } + + if (isset($closedScopes[$tokens[$inlineHTMLPtr]['code']], $tokens[$inlineHTMLPtr]['scope_closer'])) { + $inlineHTMLPtr = $tokens[$inlineHTMLPtr]['scope_closer']; + } + } while ($inlineHTMLPtr !== false && $inlineHTMLPtr < $closer); + + $currentPtr = $closer; + } while (isset(Collections::alternativeControlStructureSyntaxes()[$tokens[$closer]['code']]) === true); + + if ($hasInlineHTML === true) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'alternative syntax with inline HTML'); + } else { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'alternative syntax'); + } + + if ($hasInlineHTML === true && $this->allowWithInlineHTML === true) { + return; + } + + $error = 'Using control structures with the alternative syntax is not allowed'; + if ($this->allowWithInlineHTML === true) { + $error .= ' unless the control structure contains inline HTML'; + } + $error .= '. Found: %1$s(): ... end%1$s;'; + + $code = 'Found' . \ucfirst($tokens[$stackPtr]['content']); + if ($hasInlineHTML === true) { + $code .= 'WithInlineHTML'; + } + + $data = [$tokens[$stackPtr]['content']]; + + foreach ($chainedIssues as $opener => $closer) { + $fix = $phpcsFile->addFixableError($error, $opener, $code, $data); + } + + if ($fix === false) { + return; + } + + /* + * Fix all issues for this chain in one go to diminish the chance of conflicts. + */ + $phpcsFile->fixer->beginChangeset(); + + foreach ($chainedIssues as $opener => $closer) { + $phpcsFile->fixer->replaceToken($opener, '{'); + + if (isset(Collections::alternativeControlStructureSyntaxClosers()[$tokens[$closer]['code']]) === true) { + $phpcsFile->fixer->replaceToken($closer, '}'); + + $semicolon = $phpcsFile->findNext(Tokens::$emptyTokens, ($closer + 1), null, true); + if ($semicolon !== false && $tokens[$semicolon]['code'] === \T_SEMICOLON) { + $phpcsFile->fixer->replaceToken($semicolon, ''); + } + } else { + /* + * This must be an if/else using alternative syntax. + * The closer will be the next control structure keyword. + */ + $phpcsFile->fixer->addContentBefore($closer, '} '); + } + } + + $phpcsFile->fixer->endChangeset(); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/ControlStructures/DisallowLonelyIfSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/ControlStructures/DisallowLonelyIfSniff.php new file mode 100644 index 0000000..58c81ae --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/ControlStructures/DisallowLonelyIfSniff.php @@ -0,0 +1,348 @@ + + */ + public function register() + { + return [\T_ELSE]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + /* + * Deal with `else if`. + */ + if (ControlStructures::isElseIf($phpcsFile, $stackPtr) === true) { + // Ignore, not our real target. + return; + } + + if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) { + // Either an else without curly braces or a parse error. Ignore. + return; + } + + $outerScopeOpener = $tokens[$stackPtr]['scope_opener']; + $outerScopeCloser = $tokens[$stackPtr]['scope_closer']; + + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($outerScopeOpener + 1), $outerScopeCloser, true); + if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_IF) { + // Definitely not a lonely if statement. + return; + } + + if (isset($tokens[$nextNonEmpty]['scope_closer']) === false) { + // Either a control structure without curly braces or a parse error. Ignore. + return; + } + + /* + * Find the end of an if - else chain. + */ + + $innerIfPtr = $nextNonEmpty; + $innerIfToken = $tokens[$innerIfPtr]; + $autoFixable = true; + $innerScopeCloser = $innerIfToken['scope_closer']; + + // For alternative syntax fixer only. + // Remember the individual inner scope opener and closers so the fixer doesn't need + // to do the same walking over the if/else chain again. + $innerScopes = [ + $innerIfToken['scope_opener'] => $innerScopeCloser, + ]; + + do { + /* + * Handle control structures using alternative syntax. + */ + if ($tokens[$innerScopeCloser]['code'] !== \T_CLOSE_CURLY_BRACKET) { + if ($tokens[$innerScopeCloser]['code'] === \T_ENDIF) { + $nextAfter = $phpcsFile->findNext( + Tokens::$emptyTokens, + ($innerScopeCloser + 1), + $outerScopeCloser, + true + ); + + if ($tokens[$nextAfter]['code'] === \T_CLOSE_TAG) { + // Not "lonely" as at the very least there must be a PHP open tag before the outer closer. + return; + } + + if ($tokens[$nextAfter]['code'] === \T_SEMICOLON) { + $innerScopeCloser = $nextAfter; + } else { + // Missing semi-colon. Report, but don't auto-fix. + $autoFixable = false; + } + } else { + // This must be an else[if]. + --$innerScopeCloser; + } + } + + $innerNextNonEmpty = $phpcsFile->findNext( + Tokens::$emptyTokens, + ($innerScopeCloser + 1), + $outerScopeCloser, + true + ); + if ($innerNextNonEmpty === false) { + // This was the last closer. + break; + } + + if ($tokens[$innerNextNonEmpty]['code'] !== \T_ELSE + && $tokens[$innerNextNonEmpty]['code'] !== \T_ELSEIF + ) { + // Found another statement after the control structure. The "if" is not lonely. + return; + } + + if (isset($tokens[$innerNextNonEmpty]['scope_closer']) === false) { + // This may still be an "else if"... + $nextAfter = $phpcsFile->findNext( + Tokens::$emptyTokens, + ($innerNextNonEmpty + 1), + $outerScopeCloser, + true + ); + + if ($nextAfter === false + || $tokens[$nextAfter]['code'] !== \T_IF + || isset($tokens[$nextAfter]['scope_closer']) === false + ) { + // Defense in depth. Either a control structure without curly braces or a parse error. Ignore. + return; + } + + $innerNextNonEmpty = $nextAfter; + } + + $innerScopeCloser = $tokens[$innerNextNonEmpty]['scope_closer']; + $innerScopes[$tokens[$innerNextNonEmpty]['scope_opener']] = $innerScopeCloser; + } while (true); + + /* + * As of now, we know we have an error. Check if it can be auto-fixed. + */ + if ($phpcsFile->findNext(\T_WHITESPACE, ($innerScopeCloser + 1), $outerScopeCloser, true) !== false) { + // Comment between the inner and outer closers. + $autoFixable = false; + } + + if ($tokens[$innerScopeCloser]['code'] === \T_SEMICOLON) { + $hasComment = $phpcsFile->findPrevious(\T_WHITESPACE, ($innerScopeCloser - 1), null, true); + if ($tokens[$hasComment]['code'] !== \T_ENDIF) { + // Comment between the "endif" and the semi-colon. + $autoFixable = false; + } + } + + if ($tokens[$outerScopeOpener]['line'] !== $innerIfToken['line']) { + for ($startOfNextLine = ($outerScopeOpener + 1); $startOfNextLine < $innerIfPtr; $startOfNextLine++) { + if ($tokens[$outerScopeOpener]['line'] !== $tokens[$startOfNextLine]['line']) { + break; + } + } + + if ($phpcsFile->findNext(\T_WHITESPACE, $startOfNextLine, $innerIfPtr, true) !== false) { + // Comment between the inner and outer openers. + $autoFixable = false; + } + } + + if (isset($innerIfToken['parenthesis_opener'], $innerIfToken['parenthesis_closer']) === false) { + // Start/end of the condition of the if unclear. Most likely a parse error. + $autoFixable = false; + } + + /* + * Throw the error and potentially fix it. + */ + $error = 'If control structure block found as the only statement within an "else" block. Use elseif instead.'; + $code = 'Found'; + + if ($autoFixable === false) { + $phpcsFile->addError($error, $stackPtr, $code); + return; + } + + $fix = $phpcsFile->addFixableError($error, $stackPtr, $code); + if ($fix === false) { + return; + } + + /* + * Fix it. + */ + $outerInnerSameType = false; + if (($tokens[$outerScopeCloser]['code'] === \T_CLOSE_CURLY_BRACKET + && $tokens[$innerScopeCloser]['code'] === \T_CLOSE_CURLY_BRACKET) + || ($tokens[$outerScopeCloser]['code'] === \T_ENDIF + && $tokens[$innerScopeCloser]['code'] === \T_SEMICOLON) + ) { + $outerInnerSameType = true; + } + + $targetIsCurly = ($tokens[$outerScopeCloser]['code'] === \T_CLOSE_CURLY_BRACKET); + + $innerScopeCount = \count($innerScopes); + + $condition = GetTokensAsString::origContent($phpcsFile, ($innerIfPtr + 1), ($innerIfToken['scope_opener'] - 1)); + if ($targetIsCurly === true) { + $condition = \rtrim($condition) . ' '; + } + + $phpcsFile->fixer->beginChangeset(); + + // Remove the inner if + condition up to and including the scope opener. + for ($i = $innerIfPtr; $i <= $innerIfToken['scope_opener']; $i++) { + $phpcsFile->fixer->replaceToken($i, ''); + } + + // Potentially remove trailing whitespace/new line if there is no comment after the inner condition. + while ($tokens[$i]['line'] === $innerIfToken['line'] + && $tokens[$i]['code'] === \T_WHITESPACE + ) { + $phpcsFile->fixer->replaceToken($i, ''); + ++$i; + } + + // Remove any potential indentation whitespace for the inner if. + if ($tokens[$outerScopeOpener]['line'] !== $innerIfToken['line'] + && $tokens[$i]['line'] !== $innerIfToken['line'] + ) { + $i = ($nextNonEmpty - 1); + while ($tokens[$i]['line'] === $innerIfToken['line'] + && $tokens[$i]['code'] === \T_WHITESPACE + ) { + $phpcsFile->fixer->replaceToken($i, ''); + --$i; + } + } + + // Remove the inner scope closer. + $phpcsFile->fixer->replaceToken($innerScopeCloser, ''); + $i = ($innerScopeCloser - 1); + + // Handle alternative syntax for the closer. + if ($tokens[$innerScopeCloser]['code'] === \T_SEMICOLON) { + // Remove potential whitespace between the "endif" and the semicolon. + while ($tokens[$i]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken($i, ''); + --$i; + } + + // Remove the "endif". + $phpcsFile->fixer->replaceToken($i, ''); + --$i; + } + + // Remove superfluous whitespace before the inner scope closer. + while ($tokens[$i]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken($i, ''); + --$i; + } + + // Replace the else. + $phpcsFile->fixer->replaceToken($stackPtr, 'elseif' . $condition); + + // Remove potential superfluous whitespace between the new condition and the scope opener. + $i = ($stackPtr + 1); + while ($tokens[$i]['line'] === $tokens[$stackPtr]['line'] + && $tokens[$i]['code'] === \T_WHITESPACE + ) { + $phpcsFile->fixer->replaceToken($i, ''); + ++$i; + } + + if ($outerInnerSameType === false + && $innerScopeCount > 1 + ) { + $loop = 1; + foreach ($innerScopes as $opener => $closer) { + if ($targetIsCurly === true) { + if ($loop !== 1) { + // Only handle the opener when it's not the first of the chain as that's already handled above. + $phpcsFile->fixer->replaceToken($opener, ' {'); + } + + if ($loop !== $innerScopeCount) { + // Only handle the closer when it's not the last of the chain as that's already handled above. + $phpcsFile->fixer->addContentBefore($closer, '} '); + } + } else { + if ($loop !== 1) { + // Only handle the opener when it's not the first of the chain as that's already handled above. + $phpcsFile->fixer->replaceToken($opener, ':'); + } + + if ($loop !== $innerScopeCount) { + // Only handle the closer when it's not the last of the chain as that's already handled above. + $phpcsFile->fixer->replaceToken($closer, ''); + + $j = ($closer + 1); + while ($tokens[$j]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken($j, ''); + ++$j; + } + } + } + + ++$loop; + } + } + + $phpcsFile->fixer->endChangeset(); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/ControlStructures/IfElseDeclarationSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/ControlStructures/IfElseDeclarationSniff.php new file mode 100644 index 0000000..fb2c39d --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/ControlStructures/IfElseDeclarationSniff.php @@ -0,0 +1,164 @@ + + */ + public function register() + { + return [ + \T_ELSE, + \T_ELSEIF, + ]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + /* + * Check for control structures without braces and alternative syntax. + */ + $scopePtr = $stackPtr; + if (isset($tokens[$stackPtr]['scope_opener']) === false) { + // Deal with "else if". + $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($tokens[$next]['code'] === \T_IF) { + $scopePtr = $next; + } + } + + if (isset($tokens[$scopePtr]['scope_opener']) === false + || $tokens[$tokens[$scopePtr]['scope_opener']]['code'] === \T_COLON + ) { + // No scope opener found or alternative syntax (not our concern). + return; + } + + /* + * Check whether the else(if) is on a new line. + */ + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + if ($prevNonEmpty === false || $tokens[$prevNonEmpty]['code'] !== \T_CLOSE_CURLY_BRACKET) { + // Parse error or mixing braced and non-braced. Not our concern. + return; + } + + if ($tokens[$prevNonEmpty]['line'] !== $tokens[$stackPtr]['line']) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no'); + + $errorBase = \strtoupper($tokens[$stackPtr]['content']); + $error = $errorBase . ' statement must be on a new line.'; + + $prevNonWhitespace = $phpcsFile->findPrevious(\T_WHITESPACE, ($stackPtr - 1), null, true); + + if ($prevNonWhitespace !== $prevNonEmpty) { + // Comment found between previous scope closer and the keyword. + $fix = $phpcsFile->addError($error, $stackPtr, 'NoNewLine'); + return; + } + + $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoNewLine'); + if ($fix === false) { + return; + } + + /* + * Fix it. + */ + + // Figure out the indentation for the else(if). + $indentBase = $prevNonEmpty; + if (isset($tokens[$prevNonEmpty]['scope_condition']) === true + && ($tokens[$tokens[$prevNonEmpty]['scope_condition']]['column'] === 1 + || ($tokens[($tokens[$prevNonEmpty]['scope_condition'] - 1)]['code'] === \T_WHITESPACE + && $tokens[($tokens[$prevNonEmpty]['scope_condition'] - 1)]['column'] === 1)) + ) { + // Base the indentation off the previous if/elseif if on a line by itself. + $indentBase = $tokens[$prevNonEmpty]['scope_condition']; + } + + $indent = ''; + $firstOnIndentLine = $indentBase; + if ($tokens[$firstOnIndentLine]['column'] !== 1) { + while (isset($tokens[($firstOnIndentLine - 1)]) && $tokens[--$firstOnIndentLine]['column'] !== 1); + + if ($tokens[$firstOnIndentLine]['code'] === \T_WHITESPACE) { + $indent = $tokens[$firstOnIndentLine]['content']; + + // If tabs were replaced, use the original content. + if (isset($tokens[$firstOnIndentLine]['orig_content']) === true) { + $indent = $tokens[$firstOnIndentLine]['orig_content']; + } + } + } + + $phpcsFile->fixer->beginChangeset(); + + // Remove any whitespace between the previous scope closer and the else(if). + for ($i = ($prevNonEmpty + 1); $i < $stackPtr; $i++) { + $phpcsFile->fixer->replaceToken($i, ''); + } + + $phpcsFile->fixer->addContent($prevNonEmpty, $phpcsFile->eolChar . $indent); + $phpcsFile->fixer->endChangeset(); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Files/SeparateFunctionsFromOOSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Files/SeparateFunctionsFromOOSniff.php new file mode 100644 index 0000000..c274e75 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Files/SeparateFunctionsFromOOSniff.php @@ -0,0 +1,190 @@ + + */ + private $search = [ + // Some tokens to help skip over structures we're not interested in. + \T_START_HEREDOC => \T_START_HEREDOC, + \T_START_NOWDOC => \T_START_NOWDOC, + ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.0.0 + * + * @return array + */ + public function register() + { + $this->search += Tokens::$ooScopeTokens; + $this->search += Collections::functionDeclarationTokens(); + + return Collections::phpOpenTags(); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $firstOO = null; + $firstFunction = null; + $functionCount = 0; + $OOCount = 0; + + for ($i = 0; $i < $phpcsFile->numTokens; $i++) { + // Ignore anything within square brackets. + if ($tokens[$i]['code'] !== \T_OPEN_CURLY_BRACKET + && isset($tokens[$i]['bracket_opener'], $tokens[$i]['bracket_closer']) + && $i === $tokens[$i]['bracket_opener'] + ) { + $i = $tokens[$i]['bracket_closer']; + continue; + } + + // Skip past nested arrays, function calls and arbitrary groupings. + if ($tokens[$i]['code'] === \T_OPEN_PARENTHESIS + && isset($tokens[$i]['parenthesis_closer']) + ) { + $i = $tokens[$i]['parenthesis_closer']; + continue; + } + + // Skip over potentially large docblocks. + if ($tokens[$i]['code'] === \T_DOC_COMMENT_OPEN_TAG + && isset($tokens[$i]['comment_closer']) + ) { + $i = $tokens[$i]['comment_closer']; + continue; + } + + // Ignore everything else we're not interested in. + if (isset($this->search[$tokens[$i]['code']]) === false) { + continue; + } + + // Skip over structures which won't contain anything we're interested in. + if (($tokens[$i]['code'] === \T_START_HEREDOC + || $tokens[$i]['code'] === \T_START_NOWDOC + || $tokens[$i]['code'] === \T_ANON_CLASS + || $tokens[$i]['code'] === \T_CLOSURE + || $tokens[$i]['code'] === \T_FN) + && isset($tokens[$i]['scope_condition'], $tokens[$i]['scope_closer']) + && $tokens[$i]['scope_condition'] === $i + ) { + $i = $tokens[$i]['scope_closer']; + continue; + } + + // This will be either a function declaration or an OO declaration token. + if ($tokens[$i]['code'] === \T_FUNCTION) { + if (isset($firstFunction) === false) { + $firstFunction = $i; + } + + ++$functionCount; + } else { + if (isset($firstOO) === false) { + $firstOO = $i; + } + + ++$OOCount; + } + + if (isset($tokens[$i]['scope_closer']) === true) { + $i = $tokens[$i]['scope_closer']; + } + } + + if ($functionCount > 0 && $OOCount > 0) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'Both function and OO declarations'); + + $reportToken = \max($firstFunction, $firstOO); + + $phpcsFile->addError( + 'A file should either contain function declarations or OO structure declarations, but not both.' + . ' Found %d function declaration(s) and %d OO structure declaration(s).' + . ' The first function declaration was found on line %d;' + . ' the first OO declaration was found on line %d', + $reportToken, + 'Mixed', + [ + $functionCount, + $OOCount, + $tokens[$firstFunction]['line'], + $tokens[$firstOO]['line'], + ] + ); + } elseif ($functionCount > 0) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'Only function(s)'); + } elseif ($OOCount > 0) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'Only OO structure(s)'); + } else { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'Neither'); + } + + // Ignore the rest of the file. + return ($phpcsFile->numTokens + 1); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/FunctionDeclarations/NoLongClosuresSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/FunctionDeclarations/NoLongClosuresSniff.php new file mode 100644 index 0000000..a9ccf78 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/FunctionDeclarations/NoLongClosuresSniff.php @@ -0,0 +1,233 @@ + + */ + public function register() + { + return [\T_CLOSURE]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.1.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $this->recommendedLines = (int) $this->recommendedLines; + $this->maxLines = (int) $this->maxLines; + + $tokens = $phpcsFile->getTokens(); + + if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) { + // Live coding/parse error. Shouldn't be possible as in that case tokenizer won't retokenize to T_CLOSURE. + return; // @codeCoverageIgnore + } + + $opener = $tokens[$stackPtr]['scope_opener']; + $closer = $tokens[$stackPtr]['scope_closer']; + + $currentLine = $tokens[$opener]['line']; + $closerLine = $tokens[$closer]['line']; + + $codeLines = 0; + $commentLines = 0; + $blankLines = 0; + + // Check whether the line of the scope opener needs to be counted, but ignore trailing comments on that line. + $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($opener + 1), $closer, true); + if ($firstNonEmpty !== false && $tokens[$firstNonEmpty]['line'] === $currentLine) { + ++$codeLines; + } + + // Check whether the line of the scope closer needs to be counted. + if ($closerLine !== $currentLine) { + $hasCommentTokens = false; + $hasCodeTokens = false; + for ($i = ($closer - 1); $tokens[$i]['line'] === $closerLine && $i > $opener; $i--) { + if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === false) { + $hasCodeTokens = true; + } elseif (isset(Tokens::$commentTokens[$tokens[$i]['code']]) === true) { + $hasCommentTokens = true; + } + } + + if ($hasCodeTokens === true) { + ++$codeLines; + } elseif ($hasCommentTokens === true) { + ++$commentLines; + } + } + + // We've already examined the opener line, so move to the next line. + for ($i = ($opener + 1); $tokens[$i]['line'] === $currentLine && $i < $closer; $i++); + $currentLine = $tokens[$i]['line']; + + // Walk tokens. + while ($currentLine !== $closerLine) { + $hasCommentTokens = false; + $hasCodeTokens = false; + + while ($tokens[$i]['line'] === $currentLine) { + if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === false) { + $hasCodeTokens = true; + } elseif (isset(Tokens::$commentTokens[$tokens[$i]['code']]) === true) { + $hasCommentTokens = true; + } + + ++$i; + } + + if ($hasCodeTokens === true) { + ++$codeLines; + } elseif ($hasCommentTokens === true) { + ++$commentLines; + } else { + // Only option left is that this is an empty line. + ++$blankLines; + } + + $currentLine = $tokens[$i]['line']; + } + + $nonBlankLines = ($codeLines + $commentLines); + $totalLines = ($codeLines + $commentLines + $blankLines); + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME_CODE, $codeLines . ' lines'); + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME_COMMENTS, $nonBlankLines . ' lines'); + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME_ALL, $totalLines . ' lines'); + + $lines = $codeLines; + if ($this->ignoreCommentLines === false) { + $lines += $commentLines; + } + if ($this->ignoreEmptyLines === false) { + $lines += $blankLines; + } + + $errorSuffix = ' Declare a named function instead. Found closure containing %s lines'; + + if ($lines > $this->maxLines) { + $phpcsFile->addError( + 'Closures which are longer than %s lines are forbidden.' . $errorSuffix, + $stackPtr, + 'ExceedsMaximum', + [$this->maxLines, $lines] + ); + + return; + } + + if ($lines > $this->recommendedLines) { + $phpcsFile->addWarning( + 'It is recommended for closures to contain %s lines or less.' . $errorSuffix, + $stackPtr, + 'ExceedsRecommended', + [$this->recommendedLines, $lines] + ); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/FunctionDeclarations/RequireFinalMethodsInTraitsSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/FunctionDeclarations/RequireFinalMethodsInTraitsSniff.php new file mode 100644 index 0000000..505c19b --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/FunctionDeclarations/RequireFinalMethodsInTraitsSniff.php @@ -0,0 +1,120 @@ + + */ + public function register() + { + return [\T_FUNCTION]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.1.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + if (isset($tokens[$stackPtr]['parenthesis_opener']) === false) { + // Parse error/live coding. + return; + } + + $scopePtr = Scopes::validDirectScope($phpcsFile, $stackPtr, \T_TRAIT); + if ($scopePtr === false) { + // Not a trait method. + return; + } + + $methodProps = FunctionDeclarations::getProperties($phpcsFile, $stackPtr); + if ($methodProps['scope'] === 'private') { + // Private methods can't be final. + return; + } + + if ($methodProps['is_final'] === true) { + // Already final, nothing to do. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'final'); + return; + } + + if ($methodProps['is_abstract'] === true) { + // Abstract classes can't be final. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'abstract'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'not abstract, not final'); + + $methodName = FunctionDeclarations::getName($phpcsFile, $stackPtr); + $magic = ''; + $code = 'NonFinalMethodFound'; + if (FunctionDeclarations::isMagicMethodName($methodName) === true) { + // Use separate error code for magic methods. + $magic = 'magic '; + $code = 'NonFinalMagicMethodFound'; + } + + $data = [ + $methodProps['scope'], + $magic, + $methodName, + ObjectDeclarations::getName($phpcsFile, $scopePtr), + ]; + + $fix = $phpcsFile->addFixableError( + 'The non-abstract, %s %smethod "%s()" in trait %s should be declared as final.', + $stackPtr, + $code, + $data + ); + + if ($fix === true) { + $phpcsFile->fixer->addContentBefore($stackPtr, 'final '); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Lists/DisallowLongListSyntaxSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Lists/DisallowLongListSyntaxSniff.php new file mode 100644 index 0000000..de7447e --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Lists/DisallowLongListSyntaxSniff.php @@ -0,0 +1,71 @@ + + */ + public function register() + { + return [\T_LIST]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $openClose = Lists::getOpenClose($phpcsFile, $stackPtr); + if ($openClose === false) { + // Live coding or parse error. + return; + } + + $fix = $phpcsFile->addFixableError('Long list syntax is not allowed', $stackPtr, 'Found'); + + if ($fix === true) { + $opener = $openClose['opener']; + $closer = $openClose['closer']; + + $phpcsFile->fixer->beginChangeset(); + + $phpcsFile->fixer->replaceToken($stackPtr, ''); + $phpcsFile->fixer->replaceToken($opener, '['); + $phpcsFile->fixer->replaceToken($closer, ']'); + + $phpcsFile->fixer->endChangeset(); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Lists/DisallowShortListSyntaxSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Lists/DisallowShortListSyntaxSniff.php new file mode 100644 index 0000000..af2a948 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Lists/DisallowShortListSyntaxSniff.php @@ -0,0 +1,86 @@ + + */ + public function register() + { + return Collections::listOpenTokensBC(); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + if ($tokens[$stackPtr]['code'] === \T_LIST) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no'); + return; + } + + $openClose = Lists::getOpenClose($phpcsFile, $stackPtr); + + if ($openClose === false) { + // Not a short list, live coding or parse error. + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes'); + + $fix = $phpcsFile->addFixableError('Short list syntax is not allowed', $stackPtr, 'Found'); + + if ($fix === true) { + $opener = $openClose['opener']; + $closer = $openClose['closer']; + + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->replaceToken($opener, 'list('); + $phpcsFile->fixer->replaceToken($closer, ')'); + $phpcsFile->fixer->endChangeset(); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Namespaces/DisallowCurlyBraceSyntaxSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Namespaces/DisallowCurlyBraceSyntaxSniff.php new file mode 100644 index 0000000..3b6ad98 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Namespaces/DisallowCurlyBraceSyntaxSniff.php @@ -0,0 +1,81 @@ + + */ + public function register() + { + return [\T_NAMESPACE]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if (Namespaces::isDeclaration($phpcsFile, $stackPtr) === false) { + // Namespace operator, not a declaration; or live coding/parse error. + return; + } + + $tokens = $phpcsFile->getTokens(); + + if (isset($tokens[$stackPtr]['scope_condition']) === false + || $tokens[$stackPtr]['scope_condition'] !== $stackPtr + ) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes'); + + $phpcsFile->addError( + 'Namespace declarations using the curly brace syntax are not allowed.', + $stackPtr, + 'Forbidden' + ); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Namespaces/DisallowDeclarationWithoutNameSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Namespaces/DisallowDeclarationWithoutNameSniff.php new file mode 100644 index 0000000..5348e70 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Namespaces/DisallowDeclarationWithoutNameSniff.php @@ -0,0 +1,80 @@ + + */ + public function register() + { + return [\T_NAMESPACE]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $name = Namespaces::getDeclaredName($phpcsFile, $stackPtr); + if ($name === false) { + // Use of the namespace keyword as an operator or live coding/parse error. + return; + } + + if ($name !== '') { + // Named namespace. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no'); + + // Namespace declaration without namespace name (= global namespace). + $phpcsFile->addError( + 'Namespace declarations without a namespace name are not allowed.', + $stackPtr, + 'Forbidden' + ); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Namespaces/EnforceCurlyBraceSyntaxSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Namespaces/EnforceCurlyBraceSyntaxSniff.php new file mode 100644 index 0000000..78fa326 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Namespaces/EnforceCurlyBraceSyntaxSniff.php @@ -0,0 +1,81 @@ + + */ + public function register() + { + return [\T_NAMESPACE]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if (Namespaces::isDeclaration($phpcsFile, $stackPtr) === false) { + // Namespace operator, not a declaration; or live coding/parse error. + return; + } + + $tokens = $phpcsFile->getTokens(); + + if (isset($tokens[$stackPtr]['scope_condition']) === true + && $tokens[$stackPtr]['scope_condition'] === $stackPtr + ) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no'); + + $phpcsFile->addError( + 'Namespace declarations without curly braces are not allowed.', + $stackPtr, + 'Forbidden' + ); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Namespaces/OneDeclarationPerFileSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Namespaces/OneDeclarationPerFileSniff.php new file mode 100644 index 0000000..65f3e58 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Namespaces/OneDeclarationPerFileSniff.php @@ -0,0 +1,96 @@ + + */ + public function register() + { + return [\T_NAMESPACE]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $fileName = $phpcsFile->getFilename(); + if ($this->currentFile !== $fileName) { + // Reset the properties for each new file. + $this->currentFile = $fileName; + $this->declarationSeen = false; + } + + if (Namespaces::isDeclaration($phpcsFile, $stackPtr) === false) { + // Namespace operator, not a declaration; or live coding/parse error. + return; + } + + if ($this->declarationSeen === false) { + // This is the first namespace declaration in the file. + $this->declarationSeen = $stackPtr; + return; + } + + $tokens = $phpcsFile->getTokens(); + + // OK, so this is a file with multiple namespace declarations. + $phpcsFile->addError( + 'There should be only one namespace declaration per file. The first declaration was found on line %d', + $stackPtr, + 'MultipleFound', + [$tokens[$this->declarationSeen]['line']] + ); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/NamingConventions/NoReservedKeywordParameterNamesSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/NamingConventions/NoReservedKeywordParameterNamesSniff.php new file mode 100644 index 0000000..7815e7e --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/NamingConventions/NoReservedKeywordParameterNamesSniff.php @@ -0,0 +1,190 @@ + Key is the lowercased keyword, value the "proper" cased keyword. + */ + private $reservedNames = [ + 'abstract' => 'abstract', + 'and' => 'and', + 'array' => 'array', + 'as' => 'as', + 'break' => 'break', + 'callable' => 'callable', + 'case' => 'case', + 'catch' => 'catch', + 'class' => 'class', + 'clone' => 'clone', + 'const' => 'const', + 'continue' => 'continue', + 'declare' => 'declare', + 'default' => 'default', + 'die' => 'die', + 'do' => 'do', + 'echo' => 'echo', + 'else' => 'else', + 'elseif' => 'elseif', + 'empty' => 'empty', + 'enddeclare' => 'enddeclare', + 'endfor' => 'endfor', + 'endforeach' => 'endforeach', + 'endif' => 'endif', + 'endswitch' => 'endswitch', + 'endwhile' => 'endwhile', + 'enum' => 'enum', + 'eval' => 'eval', + 'exit' => 'exit', + 'extends' => 'extends', + 'final' => 'final', + 'finally' => 'finally', + 'fn' => 'fn', + 'for' => 'for', + 'foreach' => 'foreach', + 'function' => 'function', + 'global' => 'global', + 'goto' => 'goto', + 'if' => 'if', + 'implements' => 'implements', + 'include' => 'include', + 'include_once' => 'include_once', + 'instanceof' => 'instanceof', + 'insteadof' => 'insteadof', + 'interface' => 'interface', + 'isset' => 'isset', + 'list' => 'list', + 'match' => 'match', + 'namespace' => 'namespace', + 'new' => 'new', + 'or' => 'or', + 'print' => 'print', + 'private' => 'private', + 'protected' => 'protected', + 'public' => 'public', + 'readonly' => 'readonly', + 'require' => 'require', + 'require_once' => 'require_once', + 'return' => 'return', + 'static' => 'static', + 'switch' => 'switch', + 'throw' => 'throw', + 'trait' => 'trait', + 'try' => 'try', + 'unset' => 'unset', + 'use' => 'use', + 'var' => 'var', + 'while' => 'while', + 'xor' => 'xor', + 'yield' => 'yield', + '__class__' => '__CLASS__', + '__dir__' => '__DIR__', + '__file__' => '__FILE__', + '__function__' => '__FUNCTION__', + '__line__' => '__LINE__', + '__method__' => '__METHOD__', + '__namespace__' => '__NAMESPACE__', + '__trait__' => '__TRAIT__', + 'int' => 'int', + 'float' => 'float', + 'bool' => 'bool', + 'string' => 'string', + 'true' => 'true', + 'false' => 'false', + 'null' => 'null', + 'void' => 'void', + 'iterable' => 'iterable', + 'object' => 'object', + 'resource' => 'resource', + 'mixed' => 'mixed', + 'numeric' => 'numeric', + 'never' => 'never', + + /* + * Not reserved keywords, but equally confusing when used in the context of function calls + * with named parameters. + */ + 'parent' => 'parent', + 'self' => 'self', + ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.0.0 + * + * @return array + */ + public function register() + { + return Collections::functionDeclarationTokens(); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + // Get all parameters from method signature. + $parameters = FunctionDeclarations::getParameters($phpcsFile, $stackPtr); + if (empty($parameters)) { + return; + } + + $message = 'It is recommended not to use reserved keyword "%s" as function parameter name. Found: %s'; + + foreach ($parameters as $param) { + $name = \ltrim($param['name'], '$'); + $nameLC = \strtolower($name); + if (isset($this->reservedNames[$nameLC]) === true) { + $errorCode = $nameLC . 'Found'; + $data = [ + $this->reservedNames[$nameLC], + $param['name'], + ]; + + $phpcsFile->addWarning($message, $param['token'], $errorCode, $data); + } + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/OOStructures/AlphabeticExtendsImplementsSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/OOStructures/AlphabeticExtendsImplementsSniff.php new file mode 100644 index 0000000..4d765c2 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/OOStructures/AlphabeticExtendsImplementsSniff.php @@ -0,0 +1,275 @@ + + * class Foo implements \Vendor\DiffIterator, My\Count, DateTimeInterface {} + * + * + * If sorted using the "name" sort-order, the sniff looks just at the interface name, i.e. + * `DiffIterator`, `Count` and `DateTimeInterface`, which for this example would mean + * the correct order would be `My\Count, DateTimeInterface, \Vendor\DiffIterator`. + * + * If sorted using the "full" sort-order, the sniff will look at the full name as used + * in the `implements` statement, without leading backslashes. + * For the example above, this would mean that the correct order would be: + * `DateTimeInterface, My\Count, \Vendor\DiffIterator`. + * + * @since 1.0.0 + * + * @var string + */ + public $orderby = 'name'; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.0.0 + * + * @return array + */ + public function register() + { + return (Collections::ooCanExtend() + Collections::ooCanImplement()); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in + * the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + /* + * Validate the setting. + */ + if ($this->orderby !== 'full') { + // Use the default. + $this->orderby = 'name'; + } + $metricNameAlpha = \sprintf(self::METRIC_NAME_ALPHA, $this->orderby); + + $tokens = $phpcsFile->getTokens(); + if (isset($tokens[$stackPtr]['scope_opener']) === false) { + // Parse error or live coding. Ignore. + return; + } + + $scopeOpener = $tokens[$stackPtr]['scope_opener']; + + /* + * Get the names. + */ + if (isset(Collections::ooCanImplement()[$tokens[$stackPtr]['code']]) === true) { + $names = ObjectDeclarations::findImplementedInterfaceNames($phpcsFile, $stackPtr); + } else { + $names = ObjectDeclarations::findExtendedInterfaceNames($phpcsFile, $stackPtr); + } + + if (\is_array($names) === false) { + // Class/interface/enum doesn't extend or implement. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME_COUNT, 0); + $phpcsFile->recordMetric($stackPtr, $metricNameAlpha, 'n/a'); + return; + } + + $count = \count($names); + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME_COUNT, $count); + + if ($count < 2) { + // Nothing to sort. + $phpcsFile->recordMetric($stackPtr, $metricNameAlpha, 'n/a'); + return; + } + + /* + * Check the order. + */ + if ($this->orderby === 'name') { + $sorted = $this->sortByName($names); + } else { + $sorted = $this->sortByFull($names); + } + + if ($sorted === $names) { + // Order is already correct. + $phpcsFile->recordMetric($stackPtr, $metricNameAlpha, 'yes'); + return; + } + + $phpcsFile->recordMetric($stackPtr, $metricNameAlpha, 'no'); + + /* + * Throw the error. + */ + $keyword = \T_IMPLEMENTS; + if (isset(Collections::ooCanImplement()[$tokens[$stackPtr]['code']]) === false) { + $keyword = \T_EXTENDS; + } + + $fixable = true; + $keywordPtr = $phpcsFile->findNext($keyword, ($stackPtr + 1), $scopeOpener); + $hasComment = $phpcsFile->findNext(Tokens::$commentTokens, ($keywordPtr + 1), $scopeOpener); + if ($hasComment !== false) { + $fixable = false; + } + + $error = "The interface names in a \"%s %s\" statement should be ordered alphabetically.\n"; + $error .= 'Expected: %s; Found: %s'; + $code = \ucfirst(\strtolower($tokens[$keywordPtr]['content'])) . 'WrongOrder'; + $data = [ + $tokens[$stackPtr]['content'], + $tokens[$keywordPtr]['content'], + \implode(', ', $names), + \implode(', ', $sorted), + ]; + + if ($fixable === false) { + $code .= 'WithComments'; + $phpcsFile->addError($error, $keywordPtr, $code, $data); + return; + } + + // OK, so we appear to have a fixable error. + $fix = $phpcsFile->addFixableError($error, $keywordPtr, $code, $data); + if ($fix === false) { + return; + } + + $phpcsFile->fixer->beginChangeset(); + + // Remove the complete previous extends/implements part. + for ($i = ($keywordPtr + 1); $i < $scopeOpener; $i++) { + $phpcsFile->fixer->replaceToken($i, ''); + } + + $phpcsFile->fixer->addContent($keywordPtr, ' ' . \implode(', ', $sorted) . ' '); + + $phpcsFile->fixer->endChangeset(); + } + + /** + * Sort an array of potentially mixed qualified and unqualified names by the interface name. + * + * @since 1.0.0 + * + * @param string[] $names Interface names, potentially mixed qualified and unqualified. + * + * @return string[] + */ + protected function sortByName(array $names) + { + $getLastName = function ($name) { + $last = \strrchr($name, '\\'); + if ($last === false) { + $last = $name; + } else { + $last = \substr($last, 1); + } + + return $last; + }; + + return $this->sortNames($names, $getLastName); + } + + /** + * Sort an array of potentially mixed qualified and unqualified names by the full name. + * + * @since 1.0.0 + * + * @param string[] $names Interface names, potentially mixed qualified and unqualified. + * + * @return string[] + */ + protected function sortByFull(array $names) + { + $trimLeadingBackslash = function ($name) { + return \ltrim($name, '\\'); + }; + + return $this->sortNames($names, $trimLeadingBackslash); + } + + /** + * Sort an array of names. + * + * @since 1.0.0 + * + * @param string[] $names Interface names, potentially mixed qualified and unqualified. + * @param callable $prepareNames Function to call to prepare the names before sorting. + * + * @return string[] + */ + private function sortNames(array $names, callable $prepareNames) + { + $preppedNames = \array_map($prepareNames, $names); + $names = \array_combine($names, $preppedNames); + + \natcasesort($names); + + return \array_keys($names); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/ConcatPositionSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/ConcatPositionSniff.php new file mode 100644 index 0000000..093785a --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/ConcatPositionSniff.php @@ -0,0 +1,204 @@ + + */ + public function register() + { + return [\T_STRING_CONCAT]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.2.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process(File $phpcsFile, $stackPtr) + { + /* + * Validate the setting. + */ + if ($this->allowOnly !== self::POSITION_END) { + // Use the default. + $this->allowOnly = self::POSITION_START; + } + + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + + if ($nextNonEmpty === false) { + // Parse error/live coding. + return; + } + + $tokens = $phpcsFile->getTokens(); + if ($tokens[$prevNonEmpty]['line'] === $tokens[$nextNonEmpty]['line']) { + // Not multi-line concatenation. Not our target. + return; + } + + $position = self::POSITION_STANDALONE; + if ($tokens[$prevNonEmpty]['line'] === $tokens[$stackPtr]['line']) { + $position = self::POSITION_END; + } elseif ($tokens[$nextNonEmpty]['line'] === $tokens[$stackPtr]['line']) { + $position = self::POSITION_START; + } + + // Record metric. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, $position); + + if ($this->allowOnly === $position) { + // All okay. + return; + } + + $fix = $phpcsFile->addFixableError( + 'The concatenation operator for multi-line concatenations should always be at the %s of a line.', + $stackPtr, + 'Incorrect', + [$this->allowOnly] + ); + + if ($fix === true) { + if ($this->allowOnly === self::POSITION_END) { + $phpcsFile->fixer->beginChangeset(); + + // Move the concat operator. + $phpcsFile->fixer->replaceToken($stackPtr, ''); + $phpcsFile->fixer->addContent($prevNonEmpty, ' .'); + + if ($position === self::POSITION_START + && $tokens[($stackPtr + 1)]['code'] === \T_WHITESPACE + ) { + // Remove trailing space. + $phpcsFile->fixer->replaceToken(($stackPtr + 1), ''); + } elseif ($position === self::POSITION_STANDALONE) { + // Remove potential indentation space. + if ($tokens[($stackPtr - 1)]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken(($stackPtr - 1), ''); + } + + // Remove new line. + if ($tokens[($stackPtr + 1)]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken(($stackPtr + 1), ''); + } + } + + $phpcsFile->fixer->endChangeset(); + return; + } + + // Fixer for allowOnly === self::POSITION_START. + $phpcsFile->fixer->beginChangeset(); + + // Move the concat operator. + $phpcsFile->fixer->replaceToken($stackPtr, ''); + $phpcsFile->fixer->addContentBefore($nextNonEmpty, '. '); + + if ($position === self::POSITION_END + && $tokens[($stackPtr - 1)]['code'] === \T_WHITESPACE + ) { + // Remove trailing space. + $phpcsFile->fixer->replaceToken(($stackPtr - 1), ''); + } elseif ($position === self::POSITION_STANDALONE) { + // Remove potential indentation space. + if ($tokens[($stackPtr - 1)]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken(($stackPtr - 1), ''); + } + + // Remove new line. + if ($tokens[($stackPtr + 1)]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken(($stackPtr + 1), ''); + } + } + + $phpcsFile->fixer->endChangeset(); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/DisallowLogicalAndOrSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/DisallowLogicalAndOrSniff.php new file mode 100644 index 0000000..19e4971 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/DisallowLogicalAndOrSniff.php @@ -0,0 +1,112 @@ + + */ + private $metricType = [ + \T_LOGICAL_AND => 'logical (and/or)', + \T_LOGICAL_OR => 'logical (and/or)', + \T_BOOLEAN_AND => 'boolean (&&/||)', + \T_BOOLEAN_OR => 'boolean (&&/||)', + ]; + + /** + * The tokens this sniff targets with error code and replacements. + * + * @since 1.0.0 + * + * @var array> + */ + private $targetTokenInfo = [ + \T_LOGICAL_AND => [ + 'error_code' => 'LogicalAnd', + 'replacement' => '&&', + ], + \T_LOGICAL_OR => [ + 'error_code' => 'LogicalOr', + 'replacement' => '||', + ], + ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.0.0 + * + * @return array + */ + public function register() + { + return \array_keys($this->metricType); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $tokenCode = $tokens[$stackPtr]['code']; + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, $this->metricType[$tokenCode]); + + if (isset($this->targetTokenInfo[$tokenCode]) === false) { + // Already using boolean operator. + return; + } + + $error = 'Using logical operators is not allowed. Expected: "%s"; Found: "%s"'; + $data = [ + $this->targetTokenInfo[$tokenCode]['replacement'], + $tokens[ $stackPtr ]['content'], + ]; + + $phpcsFile->addError($error, $stackPtr, $this->targetTokenInfo[$tokenCode]['error_code'], $data); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/DisallowShortTernarySniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/DisallowShortTernarySniff.php new file mode 100644 index 0000000..37a39a1 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/DisallowShortTernarySniff.php @@ -0,0 +1,76 @@ + + */ + public function register() + { + return [\T_INLINE_THEN]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if (Operators::isShortTernary($phpcsFile, $stackPtr) === false) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'long'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'short'); + + $phpcsFile->addError( + 'Using short ternaries is not allowed as they are rarely used correctly', + $stackPtr, + 'Found' + ); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/DisallowStandalonePostIncrementDecrementSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/DisallowStandalonePostIncrementDecrementSniff.php new file mode 100644 index 0000000..b853888 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/DisallowStandalonePostIncrementDecrementSniff.php @@ -0,0 +1,197 @@ + + */ + private $allowedTokens = [ + \T_VARIABLE => \T_VARIABLE, + ]; + + /** + * Registers the tokens that this sniff wants to listen for. + * + * @since 1.0.0 + * + * @return array + */ + public function register() + { + $this->allowedTokens += Collections::ooHierarchyKeywords(); + $this->allowedTokens += Collections::objectOperators(); + $this->allowedTokens += Collections::namespacedNameTokens(); + + /* + * Remove nullsafe object operator. In/decrement not allowed in write context, + * so ignore. + */ + unset($this->allowedTokens[\T_NULLSAFE_OBJECT_OPERATOR]); + + return Collections::incrementDecrementOperators(); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + if (empty($tokens[$stackPtr]['nested_parenthesis']) === false) { + // Not a stand-alone statement. + return; + } + + $start = BCFile::findStartOfStatement($phpcsFile, $stackPtr); + $end = BCFile::findEndOfStatement($phpcsFile, $stackPtr); + + if (isset(Collections::incrementDecrementOperators()[$tokens[$end]['code']])) { + // Statement ends on a PHP close tag, set the end pointer to the close tag. + $end = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true); + } + + if ($tokens[$end]['code'] !== \T_SEMICOLON + && $tokens[$end]['code'] !== \T_CLOSE_TAG + ) { + // Not a stand-alone statement. + return $end; + } + + $counter = 0; + $lastCode = null; + $operators = Collections::incrementDecrementOperators(); + for ($i = $start; $i < $end; $i++) { + if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) { + continue; + } + + if (isset($operators[$tokens[$i]['code']]) === true) { + $lastCode = $tokens[$i]['code']; + ++$counter; + continue; + } + + if (isset($this->allowedTokens[$tokens[$i]['code']]) === true) { + $lastCode = $tokens[$i]['code']; + continue; + } + + if ($tokens[$i]['code'] === \T_OPEN_SQUARE_BRACKET + && isset($tokens[$i]['bracket_closer']) + && ($lastCode === \T_VARIABLE || $lastCode === \T_STRING) + ) { + // Array access. + $i = $tokens[$i]['bracket_closer']; + continue; + } + + // Came across an unexpected token. This is (probably) not a stand-alone statement. + return $end; + } + + if ($counter > 1) { + $phpcsFile->addWarning( + 'Using multiple increment/decrement operators in a stand-alone statement is strongly discouraged.' + . ' Found: %s', + $stackPtr, + 'MultipleOperatorsFound', + [GetTokensAsString::compact($phpcsFile, $start, ($end - 1), true)] + ); + + return $end; + } + + $type = 'increment'; + if ($tokens[$stackPtr]['code'] === \T_DEC) { + $type = 'decrement'; + } + + $lastNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($end - 1), $start, true); + if ($start === $stackPtr && $lastNonEmpty !== $stackPtr) { + // This is already pre-in/decrement. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'pre-' . $type); + return $end; + } + + if ($lastNonEmpty === false || $lastNonEmpty === $start || $lastNonEmpty !== $stackPtr) { + // Parse error or otherwise unsupported syntax. Ignore. + return $end; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'post-' . $type); + + $error = 'Stand-alone post-%1$s statement found. Use pre-%1$s instead: %2$s.'; + $errorCode = 'Post' . \ucfirst($type) . 'Found'; + $replacement = $tokens[$stackPtr]['content']; + $replacement .= GetTokensAsString::compact($phpcsFile, $start, ($lastNonEmpty - 1), true); + $data = [ + $type, + $replacement, + ]; + + $fix = $phpcsFile->addFixableError($error, $stackPtr, $errorCode, $data); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->replaceToken($stackPtr, ''); + $phpcsFile->fixer->addContentBefore($start, $tokens[$stackPtr]['content']); + $phpcsFile->fixer->endChangeset(); + } + + return $end; + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/StrictComparisonsSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/StrictComparisonsSniff.php new file mode 100644 index 0000000..c828df9 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/StrictComparisonsSniff.php @@ -0,0 +1,116 @@ + + */ + private $metricType = [ + \T_IS_EQUAL => 'loose', + \T_IS_NOT_EQUAL => 'loose', + \T_IS_IDENTICAL => 'strict', + \T_IS_NOT_IDENTICAL => 'strict', + ]; + + /** + * The tokens this sniff targets with error code and replacements. + * + * @since 1.0.0 + * + * @var array> + */ + private $targetTokenInfo = [ + \T_IS_EQUAL => [ + 'error_code' => 'LooseEqual', + 'replacement' => '===', + ], + \T_IS_NOT_EQUAL => [ + 'error_code' => 'LooseNotEqual', + 'replacement' => '!==', + ], + ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.0.0 + * + * @return array + */ + public function register() + { + return \array_keys($this->metricType); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $tokenCode = $tokens[$stackPtr]['code']; + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, $this->metricType[$tokenCode]); + + if (isset($this->targetTokenInfo[$tokenCode]) === false) { + // Already using strict comparison operator. + return; + } + + $error = 'Loose comparisons are not allowed. Expected: "%s"; Found: "%s"'; + $data = [ + $this->targetTokenInfo[$tokenCode]['replacement'], + $tokens[ $stackPtr ]['content'], + ]; + + $fix = $phpcsFile->addFixableError($error, $stackPtr, $this->targetTokenInfo[$tokenCode]['error_code'], $data); + if ($fix === false) { + return; + } + + $phpcsFile->fixer->replaceToken($stackPtr, $this->targetTokenInfo[$tokenCode]['replacement']); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/TypeSeparatorSpacingSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/TypeSeparatorSpacingSniff.php new file mode 100644 index 0000000..440ecc8 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/Operators/TypeSeparatorSpacingSniff.php @@ -0,0 +1,85 @@ + + */ + public function register() + { + return [ + \T_TYPE_UNION, + \T_TYPE_INTERSECTION, + ]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $type = ($tokens[$stackPtr]['code'] === \T_TYPE_UNION) ? 'union' : 'intersection'; + $code = \ucfirst($type) . 'Type'; + + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + SpacesFixer::checkAndFix( + $phpcsFile, + $stackPtr, + $prevNonEmpty, + 0, // Expected spaces. + 'Expected %s before the ' . $type . ' type separator. Found: %s', + $code . 'SpacesBefore', + 'error', + 0, // Severity. + 'Space before ' . $type . ' type separator' + ); + + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + SpacesFixer::checkAndFix( + $phpcsFile, + $stackPtr, + $nextNonEmpty, + 0, // Expected spaces. + 'Expected %s after the ' . $type . ' type separator. Found: %s', + $code . 'SpacesAfter', + 'error', + 0, // Severity. + 'Space after ' . $type . ' type separator' + ); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/PHP/LowercasePHPTagSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/PHP/LowercasePHPTagSniff.php new file mode 100644 index 0000000..b231b22 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/PHP/LowercasePHPTagSniff.php @@ -0,0 +1,87 @@ + + */ + public function register() + { + return [\T_OPEN_TAG]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.2.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $content = $tokens[$stackPtr]['content']; + $contentLC = \strtolower($content); + + if ($contentLC === $content) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'lowercase'); + return; + } + + $errorCode = ''; + if (\strtoupper($content) === $content) { + $errorCode = 'Uppercase'; + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'uppercase'); + } else { + $errorCode = 'Mixedcase'; + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'mixed case'); + } + + $fix = $phpcsFile->addFixableError( + 'The php open tag should be in lowercase. Found: %s', + $stackPtr, + $errorCode, + [\trim($content)] + ); + + if ($fix === true) { + $phpcsFile->fixer->replaceToken($stackPtr, $contentLC); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/PHP/OneStatementInShortEchoTagSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/PHP/OneStatementInShortEchoTagSniff.php new file mode 100644 index 0000000..cbccb4c --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/PHP/OneStatementInShortEchoTagSniff.php @@ -0,0 +1,101 @@ + + */ + public function register() + { + return [\T_OPEN_TAG_WITH_ECHO]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + for ($endOfStatement = ($stackPtr + 1); $endOfStatement < $phpcsFile->numTokens; $endOfStatement++) { + if ($tokens[$endOfStatement]['code'] === \T_CLOSE_TAG + || $tokens[$endOfStatement]['code'] === \T_SEMICOLON + ) { + break; + } + + // Skip over anything within parenthesis. + if ($tokens[$endOfStatement]['code'] === \T_OPEN_PARENTHESIS + && isset($tokens[$endOfStatement]['parenthesis_closer']) + ) { + $endOfStatement = $tokens[$endOfStatement]['parenthesis_closer']; + } + } + + if ($endOfStatement === $phpcsFile->numTokens + || $tokens[$endOfStatement]['code'] === \T_CLOSE_TAG + ) { + return; + } + + // Semi-colon, so check for any code between it and the close tag. + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($endOfStatement + 1), null, true); + if ($nextNonEmpty === false + || $tokens[$nextNonEmpty]['code'] === \T_CLOSE_TAG + ) { + return; + } + + $fix = $phpcsFile->addFixableError( + 'Only one statement is allowed when using short open echo PHP tags.' + . ' Use the "fixer->replaceToken($stackPtr, 'fixer->replaceToken($stackPtr, ' + */ + public function register() + { + return [\T_USE]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.1.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if (UseStatements::isImportUse($phpcsFile, $stackPtr) === false) { + // Closure or trait use statement. Bow out. + return; + } + + $useStatements = UseStatements::splitImportUseStatement($phpcsFile, $stackPtr); + + $ooCount = \count($useStatements['name']); + $functionCount = \count($useStatements['function']); + $constantCount = \count($useStatements['const']); + $totalCount = $ooCount + $functionCount + $constantCount; + + if ($totalCount === 0) { + // There must have been a parse error. Bow out. + return; + } + + // End of statement will always be found, otherwise the import statement parsing would have failed. + $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($stackPtr + 1)); + $groupStart = $phpcsFile->findNext(\T_OPEN_USE_GROUP, ($stackPtr + 1), $endOfStatement); + + if ($groupStart === false) { + // Not a group use statement. Just record the metric. + if ($totalCount === 1) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'single import'); + } else { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'multi import'); + } + + return; + } + + if ($totalCount === 1 + || ($ooCount !== 0 && $functionCount === 0 && $constantCount === 0) + || ($ooCount === 0 && $functionCount !== 0 && $constantCount === 0) + || ($ooCount === 0 && $functionCount === 0 && $constantCount !== 0) + ) { + // Not a *mixed* group use statement. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'group use, single type'); + return; + } + + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'group use, multi type'); + + // Build up the error message. + $foundPhrases = []; + if ($ooCount > 1) { + $foundPhrases[] = \sprintf('%d namespaces/OO names', $ooCount); + } elseif ($ooCount === 1) { + $foundPhrases[] = \sprintf('%d namespace/OO name', $ooCount); + } + + if ($functionCount > 1) { + $foundPhrases[] = \sprintf('%d functions', $functionCount); + } elseif ($functionCount === 1) { + $foundPhrases[] = \sprintf('%d function', $functionCount); + } + + if ($constantCount > 1) { + $foundPhrases[] = \sprintf('%d constants', $constantCount); + } elseif ($constantCount === 1) { + $foundPhrases[] = \sprintf('%d constant', $constantCount); + } + + if (\count($foundPhrases) === 2) { + $found = \implode(' and ', $foundPhrases); + } else { + $found = \array_shift($foundPhrases) . ', '; + $found .= \implode(' and ', $foundPhrases); + } + + $error = 'Group use statements should import one type of construct.' + . ' Mixed group use statement found importing %s.'; + $code = 'Found'; + $data = [$found]; + + $hasComment = $phpcsFile->findNext(Tokens::$commentTokens, ($stackPtr + 1), $endOfStatement); + if ($hasComment !== false) { + // Don't attempt to auto-fix is there are comments or PHPCS annotations in the statement. + $phpcsFile->addError($error, $stackPtr, $code, $data); + return; + } + + $fix = $phpcsFile->addFixableError($error, $stackPtr, $code, $data); + + if ($fix === false) { + return; + } + + /* + * Fix it. + * + * This fixer complies with the following (arbitrary) requirements: + * - It will re-use the original base "group" name, i.e. the part before \{. + * - It take take aliases into account, but only when something is aliased to a different name. + * Aliases re-using the original name will be removed. + * - The fix will not add a trailing comma after the last group use sub-statement. + * This is a PHP 7.2+ feature. + * If a standard wants to enforce trailing commas, they should use a separate sniff for that. + * - If there is only 1 statement of a certain type, the replacement will be a single + * import use statement, not a group use statement. + */ + + $phpcsFile->fixer->beginChangeset(); + + // Ensure that a potential close PHP tag ending the statement is not removed. + $tokens = $phpcsFile->getTokens(); + $endRemoval = $endOfStatement; + if ($tokens[$endOfStatement]['code'] !== \T_SEMICOLON) { + $endRemoval = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($endOfStatement - 1), null, true); + } + + // Remove old statement with the exception of the `use` keyword. + for ($i = ($stackPtr + 1); $i <= $endRemoval; $i++) { + $phpcsFile->fixer->replaceToken($i, ''); + } + + // Build up the new use import statements. + $newStatements = []; + + $useIndent = \str_repeat(' ', ($tokens[$stackPtr]['column'] - 1)); + $insideIndent = $useIndent . \str_repeat(' ', 4); + + $baseGroupName = GetTokensAsString::noEmpties($phpcsFile, ($stackPtr + 1), ($groupStart - 1)); + + foreach ($useStatements as $type => $statements) { + $count = \count($statements); + if ($count === 0) { + continue; + } + + $typeName = $type . ' '; + if ($type === 'name') { + $typeName = ''; + } + + if ($count === 1) { + $fqName = \reset($statements); + $alias = \key($statements); + + $newStatement = 'use ' . $typeName . $fqName; + + $unqualifiedName = \ltrim(\substr($fqName, \strrpos($fqName, '\\')), '\\'); + if ($unqualifiedName !== $alias) { + $newStatement .= ' as ' . $alias; + } + + $newStatement .= ';'; + + $newStatements[] = $newStatement; + continue; + } + + // Multiple statements, add a single-type group use statement. + $newStatement = 'use ' . $typeName . $baseGroupName . '{' . $phpcsFile->eolChar; + + foreach ($statements as $alias => $fqName) { + $partialName = \str_replace($baseGroupName, '', $fqName); + $newStatement .= $insideIndent . $partialName; + + $unqualifiedName = \ltrim(\substr($partialName, \strrpos($partialName, '\\')), '\\'); + if ($unqualifiedName !== $alias) { + $newStatement .= ' as ' . $alias; + } + + $newStatement .= ',' . $phpcsFile->eolChar; + } + + // Remove trailing comma after last statement as that's PHP 7.2+. + $newStatement = \rtrim($newStatement, ',' . $phpcsFile->eolChar); + + $newStatement .= $phpcsFile->eolChar . $useIndent . '};'; + $newStatements[] = $newStatement; + } + + $replacement = \implode($phpcsFile->eolChar . $useIndent, $newStatements); + + $phpcsFile->fixer->replaceToken($stackPtr, $replacement); + + $phpcsFile->fixer->endChangeset(); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/DisallowUseClassSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/DisallowUseClassSniff.php new file mode 100644 index 0000000..8dcf8dd --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/DisallowUseClassSniff.php @@ -0,0 +1,211 @@ + + */ + public function register() + { + return [ + \T_USE, + \T_NAMESPACE, + ]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $file = $phpcsFile->getFilename(); + if ($file !== $this->currentFile) { + // Reset the current namespace for each new file. + $this->currentFile = $file; + $this->currentNamespace = ''; + } + + $tokens = $phpcsFile->getTokens(); + + // Get the name of the current namespace. + if ($tokens[$stackPtr]['code'] === \T_NAMESPACE) { + $namespaceName = Namespaces::getDeclaredName($phpcsFile, $stackPtr); + if ($namespaceName !== false) { + $this->currentNamespace = $namespaceName; + } + + return; + } + + // Ok, so this is a T_USE token. + try { + $statements = UseStatements::splitImportUseStatement($phpcsFile, $stackPtr); + } catch (RuntimeException $e) { + // Not an import use statement. Bow out. + return; + } + + if (empty($statements['name'])) { + // No class/trait/interface/enum import statements found. + return; + } + + $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($stackPtr + 1)); + + foreach ($statements['name'] as $alias => $fullName) { + $reportPtr = $stackPtr; + do { + $reportPtr = $phpcsFile->findNext(\T_STRING, ($reportPtr + 1), $endOfStatement, false, $alias); + if ($reportPtr === false) { + // Shouldn't be possible. + continue 2; // @codeCoverageIgnore + } + + $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($reportPtr + 1), $endOfStatement, true); + if ($next !== false && $tokens[$next]['code'] === \T_NS_SEPARATOR) { + // Namespace level with same name. Continue searching. + continue; + } + + break; + } while (true); + + /* + * Build the error message and code. + * + * Check whether this is a non-namespaced (global) import and check whether this is an + * import from within the same namespace. + * + * Takes incorrect use statements with leading backslash into account. + * Takes case-INsensitivity of namespaces names into account. + * + * The "GlobalNamespace" error code takes precedence over the "SameNamespace" error code + * in case this is a non-namespaced file. + */ + + $error = 'Use import statements for class/interface/trait/enum%s are not allowed.'; + $error .= ' Found import statement for: "%s"'; + $errorCode = 'Found'; + $data = [ + '', + $fullName, + ]; + + $globalNamespace = false; + $sameNamespace = false; + if (\strpos($fullName, '\\', 1) === false) { + $globalNamespace = true; + $errorCode = 'FromGlobalNamespace'; + $data[0] = ' from the global namespace'; + + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'global namespace'); + } elseif ($this->currentNamespace !== '' + && (\stripos($fullName, $this->currentNamespace . '\\') === 0 + || \stripos($fullName, '\\' . $this->currentNamespace . '\\') === 0) + ) { + $sameNamespace = true; + $errorCode = 'FromSameNamespace'; + $data[0] = ' from the same namespace'; + + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'same namespace'); + } else { + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'different namespace'); + } + + $hasAlias = false; + $lastLeaf = \strtolower(\substr($fullName, -(\strlen($alias) + 1))); + $aliasLC = \strtolower($alias); + if ($lastLeaf !== $aliasLC && $lastLeaf !== '\\' . $aliasLC) { + $hasAlias = true; + $error .= ' with alias: "%s"'; + $errorCode .= 'WithAlias'; + $data[] = $alias; + + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_ALIAS, 'with alias'); + } else { + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_ALIAS, 'without alias'); + } + + if ($errorCode === 'Found') { + $errorCode = 'FoundWithoutAlias'; + } + + $phpcsFile->addError($error, $reportPtr, $errorCode, $data); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/DisallowUseConstSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/DisallowUseConstSniff.php new file mode 100644 index 0000000..44388f6 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/DisallowUseConstSniff.php @@ -0,0 +1,211 @@ + + */ + public function register() + { + return [ + \T_USE, + \T_NAMESPACE, + ]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $file = $phpcsFile->getFilename(); + if ($file !== $this->currentFile) { + // Reset the current namespace for each new file. + $this->currentFile = $file; + $this->currentNamespace = ''; + } + + $tokens = $phpcsFile->getTokens(); + + // Get the name of the current namespace. + if ($tokens[$stackPtr]['code'] === \T_NAMESPACE) { + $namespaceName = Namespaces::getDeclaredName($phpcsFile, $stackPtr); + if ($namespaceName !== false) { + $this->currentNamespace = $namespaceName; + } + + return; + } + + // Ok, so this is a T_USE token. + try { + $statements = UseStatements::splitImportUseStatement($phpcsFile, $stackPtr); + } catch (RuntimeException $e) { + // Not an import use statement. Bow out. + return; + } + + if (empty($statements['const'])) { + // No import statements for constants found. + return; + } + + $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($stackPtr + 1)); + + foreach ($statements['const'] as $alias => $fullName) { + $reportPtr = $stackPtr; + do { + $reportPtr = $phpcsFile->findNext(\T_STRING, ($reportPtr + 1), $endOfStatement, false, $alias); + if ($reportPtr === false) { + // Shouldn't be possible. + continue 2; // @codeCoverageIgnore + } + + $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($reportPtr + 1), $endOfStatement, true); + if ($next !== false && $tokens[$next]['code'] === \T_NS_SEPARATOR) { + // Namespace level with same name. Continue searching. + continue; + } + + break; + } while (true); + + /* + * Build the error message and code. + * + * Check whether this is a non-namespaced (global) import and check whether this is an + * import from within the same namespace. + * + * Takes incorrect use statements with leading backslash into account. + * Takes case-INsensitivity of namespaces names into account. + * + * The "GlobalNamespace" error code takes precedence over the "SameNamespace" error code + * in case this is a non-namespaced file. + */ + + $error = 'Use import statements for constants%s are not allowed.'; + $error .= ' Found import statement for: "%s"'; + $errorCode = 'Found'; + $data = [ + '', + $fullName, + ]; + + $globalNamespace = false; + $sameNamespace = false; + if (\strpos($fullName, '\\', 1) === false) { + $globalNamespace = true; + $errorCode = 'FromGlobalNamespace'; + $data[0] = ' from the global namespace'; + + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'global namespace'); + } elseif ($this->currentNamespace !== '' + && (\stripos($fullName, $this->currentNamespace . '\\') === 0 + || \stripos($fullName, '\\' . $this->currentNamespace . '\\') === 0) + ) { + $sameNamespace = true; + $errorCode = 'FromSameNamespace'; + $data[0] = ' from the same namespace'; + + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'same namespace'); + } else { + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'different namespace'); + } + + $hasAlias = false; + $lastLeaf = \strtolower(\substr($fullName, -(\strlen($alias) + 1))); + $aliasLC = \strtolower($alias); + if ($lastLeaf !== $aliasLC && $lastLeaf !== '\\' . $aliasLC) { + $hasAlias = true; + $error .= ' with alias: "%s"'; + $errorCode .= 'WithAlias'; + $data[] = $alias; + + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_ALIAS, 'with alias'); + } else { + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_ALIAS, 'without alias'); + } + + if ($errorCode === 'Found') { + $errorCode = 'FoundWithoutAlias'; + } + + $phpcsFile->addError($error, $reportPtr, $errorCode, $data); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/DisallowUseFunctionSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/DisallowUseFunctionSniff.php new file mode 100644 index 0000000..46a39ed --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/DisallowUseFunctionSniff.php @@ -0,0 +1,211 @@ + + */ + public function register() + { + return [ + \T_USE, + \T_NAMESPACE, + ]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $file = $phpcsFile->getFilename(); + if ($file !== $this->currentFile) { + // Reset the current namespace for each new file. + $this->currentFile = $file; + $this->currentNamespace = ''; + } + + $tokens = $phpcsFile->getTokens(); + + // Get the name of the current namespace. + if ($tokens[$stackPtr]['code'] === \T_NAMESPACE) { + $namespaceName = Namespaces::getDeclaredName($phpcsFile, $stackPtr); + if ($namespaceName !== false) { + $this->currentNamespace = $namespaceName; + } + + return; + } + + // Ok, so this is a T_USE token. + try { + $statements = UseStatements::splitImportUseStatement($phpcsFile, $stackPtr); + } catch (RuntimeException $e) { + // Not an import use statement. Bow out. + return; + } + + if (empty($statements['function'])) { + // No import statements for functions found. + return; + } + + $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($stackPtr + 1)); + + foreach ($statements['function'] as $alias => $fullName) { + $reportPtr = $stackPtr; + do { + $reportPtr = $phpcsFile->findNext(\T_STRING, ($reportPtr + 1), $endOfStatement, false, $alias); + if ($reportPtr === false) { + // Shouldn't be possible. + continue 2; // @codeCoverageIgnore + } + + $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($reportPtr + 1), $endOfStatement, true); + if ($next !== false && $tokens[$next]['code'] === \T_NS_SEPARATOR) { + // Namespace level with same name. Continue searching. + continue; + } + + break; + } while (true); + + /* + * Build the error message and code. + * + * Check whether this is a non-namespaced (global) import and check whether this is an + * import from within the same namespace. + * + * Takes incorrect use statements with leading backslash into account. + * Takes case-INsensitivity of namespaces names into account. + * + * The "GlobalNamespace" error code takes precedence over the "SameNamespace" error code + * in case this is a non-namespaced file. + */ + + $error = 'Use import statements for functions%s are not allowed.'; + $error .= ' Found import statement for: "%s"'; + $errorCode = 'Found'; + $data = [ + '', + $fullName, + ]; + + $globalNamespace = false; + $sameNamespace = false; + if (\strpos($fullName, '\\', 1) === false) { + $globalNamespace = true; + $errorCode = 'FromGlobalNamespace'; + $data[0] = ' from the global namespace'; + + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'global namespace'); + } elseif ($this->currentNamespace !== '' + && (\stripos($fullName, $this->currentNamespace . '\\') === 0 + || \stripos($fullName, '\\' . $this->currentNamespace . '\\') === 0) + ) { + $sameNamespace = true; + $errorCode = 'FromSameNamespace'; + $data[0] = ' from the same namespace'; + + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'same namespace'); + } else { + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'different namespace'); + } + + $hasAlias = false; + $lastLeaf = \strtolower(\substr($fullName, -(\strlen($alias) + 1))); + $aliasLC = \strtolower($alias); + if ($lastLeaf !== $aliasLC && $lastLeaf !== '\\' . $aliasLC) { + $hasAlias = true; + $error .= ' with alias: "%s"'; + $errorCode .= 'WithAlias'; + $data[] = $alias; + + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_ALIAS, 'with alias'); + } else { + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_ALIAS, 'without alias'); + } + + if ($errorCode === 'Found') { + $errorCode = 'FoundWithoutAlias'; + } + + $phpcsFile->addError($error, $reportPtr, $errorCode, $data); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/KeywordSpacingSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/KeywordSpacingSniff.php new file mode 100644 index 0000000..3dd47c0 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/KeywordSpacingSniff.php @@ -0,0 +1,207 @@ + + */ + protected $keywords = [ + 'const' => true, + 'function' => true, + ]; + + /** + * Returns an array of tokens this sniff wants to listen for. + * + * @since 1.1.0 + * + * @return array + */ + public function register() + { + return [\T_USE]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.1.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the + * stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if (UseStatements::isImportUse($phpcsFile, $stackPtr) === false) { + // Trait or closure use statement. + return; + } + + $tokens = $phpcsFile->getTokens(); + $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($stackPtr + 1)); + if ($endOfStatement === false) { + // Live coding or parse error. + return; + } + + // Check the spacing after the `use` keyword. + $this->checkSpacingAfterKeyword($phpcsFile, $stackPtr, $tokens[$stackPtr]['content']); + + // Check the spacing before and after each `as` keyword. + $current = $stackPtr; + do { + $current = $phpcsFile->findNext(\T_AS, ($current + 1), $endOfStatement); + if ($current === false) { + break; + } + + // Prevent false positives when "as" is used within a "name". + if (isset(Tokens::$emptyTokens[$tokens[($current - 1)]['code']]) === true) { + $this->checkSpacingBeforeKeyword($phpcsFile, $current, $tokens[$current]['content']); + $this->checkSpacingAfterKeyword($phpcsFile, $current, $tokens[$current]['content']); + } + } while (true); + + /* + * Check the spacing after `function` and `const` keywords. + */ + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if (isset($this->keywords[\strtolower($tokens[$nextNonEmpty]['content'])]) === true) { + // Keyword found at start of statement, applies to whole statement. + $this->checkSpacingAfterKeyword($phpcsFile, $nextNonEmpty, $tokens[$nextNonEmpty]['content']); + return; + } + + // This may still be a group use statement with function/const substatements. + $openGroup = $phpcsFile->findNext(\T_OPEN_USE_GROUP, ($stackPtr + 1), $endOfStatement); + if ($openGroup === false) { + // Not a group use statement. + return; + } + + $closeGroup = $phpcsFile->findNext(\T_CLOSE_USE_GROUP, ($openGroup + 1), $endOfStatement); + + $current = $openGroup; + do { + $current = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), $closeGroup, true); + if ($current === false) { + return; + } + + if (isset($this->keywords[\strtolower($tokens[$current]['content'])]) === true) { + $this->checkSpacingAfterKeyword($phpcsFile, $current, $tokens[$current]['content']); + } + + // We're within the use group, so find the next comma. + $current = $phpcsFile->findNext(\T_COMMA, ($current + 1), $closeGroup); + } while ($current !== false); + } + + /** + * Check the spacing before a found keyword. + * + * @since 1.1.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the keyword in the token stack. + * @param string $content The keyword as found. + * + * @return void + */ + public function checkSpacingBeforeKeyword(File $phpcsFile, $stackPtr, $content) + { + $contentLC = \strtolower($content); + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + + SpacesFixer::checkAndFix( + $phpcsFile, + $stackPtr, + $prevNonEmpty, + 1, // Expected spaces. + 'Expected %s before the "' . $contentLC . '" keyword. Found: %s', + 'SpaceBefore' . \ucfirst($contentLC), + 'error', + 0, // Severity. + \sprintf(self::METRIC_NAME_BEFORE, $contentLC) + ); + } + + /** + * Check the spacing after a found keyword. + * + * @since 1.1.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the keyword in the token stack. + * @param string $content The keyword as found. + * + * @return void + */ + public function checkSpacingAfterKeyword(File $phpcsFile, $stackPtr, $content) + { + $contentLC = \strtolower($content); + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + + SpacesFixer::checkAndFix( + $phpcsFile, + $stackPtr, + $nextNonEmpty, + 1, // Expected spaces. + 'Expected %s after the "' . $contentLC . '" keyword. Found: %s', + 'SpaceAfter' . \ucfirst($contentLC), + 'error', + 0, // Severity. + \sprintf(self::METRIC_NAME_AFTER, $contentLC) + ); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/LowercaseFunctionConstSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/LowercaseFunctionConstSniff.php new file mode 100644 index 0000000..b9f87f2 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/LowercaseFunctionConstSniff.php @@ -0,0 +1,156 @@ + + */ + protected $keywords = [ + 'const' => true, + 'function' => true, + ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.0.0 + * + * @return array + */ + public function register() + { + return [\T_USE]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the + * stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if (UseStatements::isImportUse($phpcsFile, $stackPtr) === false) { + // Trait or closure use statement. + return; + } + + $tokens = $phpcsFile->getTokens(); + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($nextNonEmpty === false) { + // Live coding or parse error. + return; + } + + if (isset($this->keywords[\strtolower($tokens[$nextNonEmpty]['content'])]) === true) { + // Keyword found at start of statement, applies to whole statement. + $this->processKeyword($phpcsFile, $nextNonEmpty, $tokens[$nextNonEmpty]['content']); + return; + } + + // This may still be a group use statement with function/const substatements. + $openGroup = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG, \T_OPEN_USE_GROUP], ($stackPtr + 1)); + if ($openGroup === false || $tokens[$openGroup]['code'] !== \T_OPEN_USE_GROUP) { + // Not a group use statement. + return; + } + + $closeGroup = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG, \T_CLOSE_USE_GROUP], ($openGroup + 1)); + if ($closeGroup === false || $tokens[$closeGroup]['code'] !== \T_CLOSE_USE_GROUP) { + // Live coding or parse error. + return; + } + + $current = $openGroup; + do { + $current = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), $closeGroup, true); + if ($current === false) { + return; + } + + if (isset($this->keywords[\strtolower($tokens[$current]['content'])]) === true) { + $this->processKeyword($phpcsFile, $current, $tokens[$current]['content']); + } + + // We're within the use group, so find the next comma. + $current = $phpcsFile->findNext(\T_COMMA, ($current + 1), $closeGroup); + } while ($current !== false); + } + + /** + * Processes a found keyword. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the keyword in the token stack. + * @param string $content The keyword as found. + * + * @return void + */ + public function processKeyword(File $phpcsFile, $stackPtr, $content) + { + $contentLC = \strtolower($content); + $metricName = \sprintf(self::METRIC_NAME, $contentLC); + if ($contentLC === $content) { + // Already lowercase. Bow out. + $phpcsFile->recordMetric($stackPtr, $metricName, 'lowercase'); + return; + } + + if (\strtoupper($content) === $content) { + $phpcsFile->recordMetric($stackPtr, $metricName, 'uppercase'); + } else { + $phpcsFile->recordMetric($stackPtr, $metricName, 'mixed case'); + } + + $error = 'The "%s" keyword when used in an import use statements must be lowercase.'; + $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotLowercase', [$contentLC]); + + if ($fix === true) { + $phpcsFile->fixer->replaceToken($stackPtr, $contentLC); + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/NoLeadingBackslashSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/NoLeadingBackslashSniff.php new file mode 100644 index 0000000..1fccc82 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/NoLeadingBackslashSniff.php @@ -0,0 +1,170 @@ + + */ + public function register() + { + return [\T_USE]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the + * stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if (UseStatements::isImportUse($phpcsFile, $stackPtr) === false) { + // Trait or closure use statement. + return; + } + + $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG, \T_OPEN_USE_GROUP], ($stackPtr + 1)); + if ($endOfStatement === false) { + // Live coding or parse error. + return; + } + + $tokens = $phpcsFile->getTokens(); + $current = $stackPtr; + + do { + $continue = $this->processImport($phpcsFile, $current, $endOfStatement); + if ($continue === false) { + break; + } + + // Move the stackPtr forward to the next part of the use statement, if any. + $current = $phpcsFile->findNext(\T_COMMA, ($current + 1), $endOfStatement); + } while ($current !== false); + + if ($tokens[$endOfStatement]['code'] !== \T_OPEN_USE_GROUP) { + // Finished the statement. + return; + } + + $current = $endOfStatement; // Group open brace. + $endOfStatement = $phpcsFile->findNext([\T_CLOSE_USE_GROUP], ($endOfStatement + 1)); + if ($endOfStatement === false) { + // Live coding or parse error. + return; + } + + do { + $continue = $this->processImport($phpcsFile, $current, $endOfStatement, true); + if ($continue === false) { + break; + } + + // Move the stackPtr forward to the next part of the use statement, if any. + $current = $phpcsFile->findNext(\T_COMMA, ($current + 1), $endOfStatement); + } while ($current !== false); + } + + /** + * Examine an individual import statement. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token. + * @param int $endOfStatement End token for the current import statement. + * @param bool $groupUse Whether the current statement is a partial one + * within a group use statement. + * + * @return bool Whether or not to continue examining this import use statement. + */ + private function processImport(File $phpcsFile, $stackPtr, $endOfStatement, $groupUse = false) + { + $tokens = $phpcsFile->getTokens(); + + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), $endOfStatement, true); + if ($nextNonEmpty === false) { + // Reached the end of the statement. + return false; + } + + // Skip past 'function'/'const' keyword. + $contentLC = \strtolower($tokens[$nextNonEmpty]['content']); + if ($tokens[$nextNonEmpty]['code'] === \T_STRING + && ($contentLC === 'function' || $contentLC === 'const') + ) { + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), $endOfStatement, true); + if ($nextNonEmpty === false) { + // Reached the end of the statement. + return false; + } + } + + if ($tokens[$nextNonEmpty]['code'] === \T_NS_SEPARATOR) { + $phpcsFile->recordMetric($nextNonEmpty, self::METRIC_NAME, 'yes'); + + $error = 'An import use statement should never start with a leading backslash'; + $code = 'LeadingBackslashFound'; + + if ($groupUse === true) { + $error = 'Parse error: partial import use statement in a use group starting with a leading backslash'; + $code = 'LeadingBackslashFoundInGroup'; + } + + $fix = $phpcsFile->addFixableError($error, $nextNonEmpty, $code); + + if ($fix === true) { + if ($tokens[$nextNonEmpty - 1]['code'] !== \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken($nextNonEmpty, ' '); + } else { + $phpcsFile->fixer->replaceToken($nextNonEmpty, ''); + } + } + } else { + $phpcsFile->recordMetric($nextNonEmpty, self::METRIC_NAME, 'no'); + } + + return true; + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/NoUselessAliasesSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/NoUselessAliasesSniff.php new file mode 100644 index 0000000..93de596 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/UseStatements/NoUselessAliasesSniff.php @@ -0,0 +1,155 @@ + + */ + public function register() + { + return [\T_USE]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.1.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if (UseStatements::isImportUse($phpcsFile, $stackPtr) === false) { + // Closure or trait use statement. Bow out. + return; + } + + $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($stackPtr + 1)); + if ($endOfStatement === false) { + // Parse error or live coding. + return; + } + + $hasAliases = $phpcsFile->findNext(\T_AS, ($stackPtr + 1), $endOfStatement); + if ($hasAliases === false) { + // This use import statement does not alias anything, bow out. + return; + } + + $useStatements = UseStatements::splitImportUseStatement($phpcsFile, $stackPtr); + if (\count($useStatements, \COUNT_RECURSIVE) <= 3) { + // No statements found. Shouldn't be possible, but still. Bow out. + return; + } + + $tokens = $phpcsFile->getTokens(); + + // Collect all places where aliases are used in this use statement. + $aliasPtrs = []; + $currentAs = $hasAliases; + do { + $aliasPtr = $phpcsFile->findNext(Tokens::$emptyTokens, ($currentAs + 1), null, true); + if ($aliasPtr !== false && $tokens[$aliasPtr]['code'] === \T_STRING) { + $aliasPtrs[$currentAs] = $aliasPtr; + } + + $currentAs = $phpcsFile->findNext(\T_AS, ($currentAs + 1), $endOfStatement); + } while ($currentAs !== false); + + // Now check the names in each use statement for useless aliases. + foreach ($useStatements as $type => $statements) { + foreach ($statements as $alias => $fqName) { + $unqualifiedName = \ltrim(\substr($fqName, \strrpos($fqName, '\\')), '\\'); + + $uselessAlias = false; + if ($type === 'const') { + // Do a case-sensitive comparison for constants. + if ($unqualifiedName === $alias) { + $uselessAlias = true; + } + } elseif (NamingConventions::isEqual($unqualifiedName, $alias)) { + $uselessAlias = true; + } + + if ($uselessAlias === false) { + continue; + } + + // Now check if this is actually used as an alias or just the actual name. + foreach ($aliasPtrs as $asPtr => $aliasPtr) { + if ($tokens[$aliasPtr]['content'] !== $alias) { + continue; + } + + // Make sure this is really the right one. + $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($asPtr - 1), null, true); + if ($tokens[$prev]['code'] !== \T_STRING + || $tokens[$prev]['content'] !== $unqualifiedName + ) { + continue; + } + + $error = 'Useless alias "%s" found for import of "%s"'; + $code = 'Found'; + $data = [$alias, $fqName]; + + // Okay, so this is the one which should be flagged. + $hasComments = $phpcsFile->findNext(Tokens::$commentTokens, ($prev + 1), $aliasPtr); + if ($hasComments !== false) { + // Don't auto-fix if there are comments. + $phpcsFile->addError($error, $aliasPtr, $code, $data); + break; + } + + $fix = $phpcsFile->addFixableError($error, $aliasPtr, $code, $data); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + for ($i = ($prev + 1); $i <= $aliasPtr; $i++) { + $phpcsFile->fixer->replaceToken($i, ''); + } + + $phpcsFile->fixer->endChangeset(); + } + + break; + } + } + } + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/WhiteSpace/AnonClassKeywordSpacingSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/WhiteSpace/AnonClassKeywordSpacingSniff.php new file mode 100644 index 0000000..3116316 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/WhiteSpace/AnonClassKeywordSpacingSniff.php @@ -0,0 +1,79 @@ + + */ + public function register() + { + return [\T_ANON_CLASS]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) { + // No parentheses, nothing to do. + return; + } + + SpacesFixer::checkAndFix( + $phpcsFile, + $stackPtr, + $nextNonEmpty, + (int) $this->spacing, + 'There must be %1$s between the class keyword and the open parenthesis for an anonymous class. Found: %2$s', + 'Incorrect', + 'error', + 0, + 'Anon class: space between keyword and open parenthesis' + ); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/WhiteSpace/CommaSpacingSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/WhiteSpace/CommaSpacingSniff.php new file mode 100644 index 0000000..376e904 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/WhiteSpace/CommaSpacingSniff.php @@ -0,0 +1,408 @@ + + */ + public function register() + { + return [\T_COMMA]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.1.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + if (isset($this->phpVersion) === false || \defined('PHP_CODESNIFFER_IN_TESTS')) { + // Set default value to prevent this code from running every time the sniff is triggered. + $this->phpVersion = 0; + + $phpVersion = Helper::getConfigData('php_version'); + if ($phpVersion !== null) { + $this->phpVersion = (int) $phpVersion; + } + } + + $this->processSpacingBefore($phpcsFile, $stackPtr); + $this->processSpacingAfter($phpcsFile, $stackPtr); + } + + /** + * Check the spacing before the comma. + * + * @since 1.1.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + protected function processSpacingBefore(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $prevNonWhitespace = $phpcsFile->findPrevious(\T_WHITESPACE, ($stackPtr - 1), null, true); + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + + if ($prevNonWhitespace !== $prevNonEmpty + && $tokens[$prevNonEmpty]['code'] !== \T_COMMA + && $tokens[$prevNonEmpty]['line'] !== $tokens[$nextNonEmpty]['line'] + ) { + // Special case: comma after a trailing comment - the comma should be moved to before the comment. + $fix = $phpcsFile->addFixableError( + 'Comma found after comment, expected the comma after the end of the code', + $stackPtr, + 'CommaAfterComment' + ); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + $phpcsFile->fixer->replaceToken($stackPtr, ''); + $phpcsFile->fixer->addContent($prevNonEmpty, ','); + + // Clean up potential trailing whitespace left behind, but don't remove blank lines. + $nextNonWhitespace = $phpcsFile->findNext(\T_WHITESPACE, ($stackPtr + 1), null, true); + if ($tokens[($stackPtr - 1)]['code'] === \T_WHITESPACE + && $tokens[($stackPtr - 1)]['line'] === $tokens[$stackPtr]['line'] + && $tokens[$stackPtr]['line'] !== $tokens[$nextNonWhitespace]['line'] + ) { + $phpcsFile->fixer->replaceToken(($stackPtr - 1), ''); + } + + $phpcsFile->fixer->endChangeset(); + } + return; + } + + if ($tokens[$prevNonWhitespace]['code'] === \T_COMMA) { + // This must be a list assignment with ignored items. Ignore. + return; + } + + if (isset(Tokens::$blockOpeners[$tokens[$prevNonWhitespace]['code']]) === true + || $tokens[$prevNonWhitespace]['code'] === \T_OPEN_SHORT_ARRAY + || $tokens[$prevNonWhitespace]['code'] === \T_OPEN_USE_GROUP + ) { + // Should only realistically be possible for lists. Leave for a block brace spacing sniff to sort out. + return; + } + + $expectedSpaces = 0; + + if ($tokens[$prevNonEmpty]['code'] === \T_END_HEREDOC + || $tokens[$prevNonEmpty]['code'] === \T_END_NOWDOC + ) { + /* + * If php_version is explicitly set to PHP < 7.3, enforce a new line between the closer and the comma. + * + * If php_version is *not* explicitly set, let the indent be leading and only enforce + * a new line between the closer and the comma when this is an old-style heredoc/nowdoc. + */ + if ($this->phpVersion !== 0 && $this->phpVersion < 70300) { + $expectedSpaces = 'newline'; + } + + if ($this->phpVersion === 0 + && \ltrim($tokens[$prevNonEmpty]['content']) === $tokens[$prevNonEmpty]['content'] + ) { + $expectedSpaces = 'newline'; + } + } + + $error = 'Expected %1$s between "' . $this->escapePlaceholders($tokens[$prevNonWhitespace]['content']) + . '" and the comma. Found: %2$s'; + $codeSuffix = $this->getSuffix($phpcsFile, $stackPtr); + $metricSuffix = $this->codeSuffixToMetric($codeSuffix); + + SpacesFixer::checkAndFix( + $phpcsFile, + $stackPtr, + $prevNonWhitespace, + $expectedSpaces, + $error, + 'SpaceBefore' . $codeSuffix, + 'error', + 0, + self::METRIC_NAME_BEFORE . $metricSuffix + ); + } + + /** + * Check the spacing after the comma. + * + * @since 1.1.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + protected function processSpacingAfter(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $nextNonWhitespace = $phpcsFile->findNext(\T_WHITESPACE, ($stackPtr + 1), null, true); + if ($nextNonWhitespace === false) { + // Live coding/parse error. Ignore. + return; + } + + if ($tokens[$nextNonWhitespace]['code'] === \T_COMMA) { + // This must be a list assignment with ignored items. Ignore. + return; + } + + if ($tokens[$nextNonWhitespace]['code'] === \T_CLOSE_CURLY_BRACKET + || $tokens[$nextNonWhitespace]['code'] === \T_CLOSE_SQUARE_BRACKET + || $tokens[$nextNonWhitespace]['code'] === \T_CLOSE_PARENTHESIS + || $tokens[$nextNonWhitespace]['code'] === \T_CLOSE_SHORT_ARRAY + || $tokens[$nextNonWhitespace]['code'] === \T_CLOSE_USE_GROUP + ) { + // Ignore. Leave for a block spacing sniff to sort out. + return; + } + + $nextToken = $tokens[($stackPtr + 1)]; + + $error = 'Expected %1$s between the comma and "' + . $this->escapePlaceholders($tokens[$nextNonWhitespace]['content']) . '". Found: %2$s'; + + $codeSuffix = $this->getSuffix($phpcsFile, $stackPtr); + $metricSuffix = $this->codeSuffixToMetric($codeSuffix); + + if ($nextToken['code'] === \T_WHITESPACE) { + if ($nextToken['content'] === ' ') { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME_AFTER . $metricSuffix, '1 space'); + return; + } + + // Note: this check allows for trailing whitespace between the comma and a new line char. + // The trailing whitespace is not the concern of this sniff. + if (\ltrim($nextToken['content'], ' ') === $phpcsFile->eolChar) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME_AFTER . $metricSuffix, 'a new line'); + return; + } + + $errorCode = 'TooMuchSpaceAfter' . $codeSuffix; + + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if (isset(Tokens::$commentTokens[$tokens[$nextNonWhitespace]['code']]) === true + && ($nextNonEmpty === false || $tokens[$stackPtr]['line'] !== $tokens[$nextNonEmpty]['line']) + ) { + // Separate error code to allow for aligning trailing comments. + $errorCode = 'TooMuchSpaceAfterCommaBeforeTrailingComment'; + } + + SpacesFixer::checkAndFix( + $phpcsFile, + $stackPtr, + $nextNonWhitespace, + 1, + $error, + $errorCode, + 'error', + 0, + self::METRIC_NAME_AFTER . $metricSuffix + ); + return; + } + + SpacesFixer::checkAndFix( + $phpcsFile, + $stackPtr, + $nextNonWhitespace, + 1, + $error, + 'NoSpaceAfter' . $codeSuffix, + 'error', + 0, + self::METRIC_NAME_AFTER . $metricSuffix + ); + } + + /** + * Escape arbitrary token content for *printf() placeholders. + * + * @since 1.1.0 + * + * @param string $text Arbitrary text string. + * + * @return string + */ + private function escapePlaceholders($text) + { + return \preg_replace('`(?:^|[^%])(%)(?:[^%]|$)`', '%%', \trim($text)); + } + + /** + * Retrieve a text string for use as a suffix to an error code. + * + * This allows for modular error codes, which in turn allow for selectively excluding + * error codes. + * + * {@internal Closure use will be parentheses owner in PHPCS 4.x, this code will + * need an update for that in due time.} + * + * @since 1.1.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return string + */ + private function getSuffix($phpcsFile, $stackPtr) + { + $opener = Parentheses::getLastOpener($phpcsFile, $stackPtr); + if ($opener === false) { + return ''; + } + + $tokens = $phpcsFile->getTokens(); + + $owner = Parentheses::getOwner($phpcsFile, $opener); + if ($owner !== false) { + switch ($tokens[$owner]['code']) { + case \T_FUNCTION: + case \T_CLOSURE: + case \T_FN: + return 'InFunctionDeclaration'; + + case \T_DECLARE: + return 'InDeclare'; + + case \T_ANON_CLASS: + case \T_ISSET: + case \T_UNSET: + return 'InFunctionCall'; + + // Long array, long list, isset, unset, empty, exit, eval, control structures. + default: + return ''; + } + } + + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), null, true); + + if (isset(Collections::nameTokens()[$tokens[$prevNonEmpty]['code']]) === true) { + return 'InFunctionCall'; + } + + switch ($tokens[$prevNonEmpty]['code']) { + case \T_USE: + return 'InClosureUse'; + + case \T_VARIABLE: + case \T_SELF: + case \T_STATIC: + case \T_PARENT: + return 'InFunctionCall'; + + default: + return ''; + } + } + + /** + * Transform a suffix for an error code into a suffix for a metric. + * + * @since 1.1.0 + * + * @param string $suffix Error code suffix. + * + * @return string + */ + private function codeSuffixToMetric($suffix) + { + return \strtolower(\preg_replace('`([A-Z])`', ' $1', $suffix)); + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/WhiteSpace/DisallowInlineTabsSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/WhiteSpace/DisallowInlineTabsSniff.php new file mode 100644 index 0000000..817a44e --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/WhiteSpace/DisallowInlineTabsSniff.php @@ -0,0 +1,173 @@ + + * + * + * + * The PHPCS native `Generic.Whitespace.DisallowTabIndent` sniff oversteps its reach and silently + * does mid-line tab to space replacements as well. + * However, the sister-sniff `Generic.Whitespace.DisallowSpaceIndent` leaves mid-line tabs/spaces alone. + * This sniff fills that gap. + * + * @since 1.0.0 + */ +final class DisallowInlineTabsSniff implements Sniff +{ + + /** + * The --tab-width CLI value that is being used. + * + * @since 1.0.0 + * + * @var int + */ + private $tabWidth; + + /** + * Tokens to check for mid-line tabs. + * + * @since 1.0.0 + * + * @var array + */ + private $find = [ + \T_WHITESPACE => true, + \T_DOC_COMMENT_WHITESPACE => true, + \T_DOC_COMMENT_STRING => true, + \T_COMMENT => true, + ]; + + /** + * Registers the tokens that this sniff wants to listen for. + * + * @since 1.0.0 + * + * @return array + */ + public function register() + { + return Collections::phpOpenTags(); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return int Integer stack pointer to skip the rest of the file. + */ + public function process(File $phpcsFile, $stackPtr) + { + if (isset($this->tabWidth) === false) { + $this->tabWidth = (int) Helper::getTabWidth($phpcsFile); + } + + if (\defined('PHP_CODESNIFFER_IN_TESTS')) { + $this->tabWidth = (int) Helper::getCommandLineData($phpcsFile, 'tabWidth'); + } + + $tokens = $phpcsFile->getTokens(); + $dummy = new DummyTokenizer('', $phpcsFile->config); + + for ($i = 0; $i < $phpcsFile->numTokens; $i++) { + // Skip all non-whitespace tokens and skip whitespace at the start of a new line. + if (isset($this->find[$tokens[$i]['code']]) === false + || (($tokens[$i]['code'] === \T_WHITESPACE + || $tokens[$i]['code'] === \T_DOC_COMMENT_WHITESPACE) + && $tokens[$i]['column'] === 1) + ) { + continue; + } + + // If tabs haven't been converted to spaces by the tokenizer, do so now. + $token = $tokens[$i]; + if (isset($token['orig_content']) === false) { + if ($token['content'] === '' || \strpos($token['content'], "\t") === false) { + // If there are no tabs, we can continue, no matter what. + continue; + } + + $dummy->replaceTabsInToken($token); + } + + /* + * Tokens only have the 'orig_content' key if they contain tabs, + * so from here on out, we **know** there will be tabs in the content. + */ + $origContent = $token['orig_content']; + $commentOnly = ''; + + $multiLineComment = false; + if (($tokens[$i]['code'] === \T_COMMENT + || isset(Tokens::$phpcsCommentTokens[$tokens[$i]['code']])) + && $tokens[$i]['column'] === 1 + && ($tokens[($i - 1)]['code'] === \T_COMMENT + || isset(Tokens::$phpcsCommentTokens[$tokens[($i - 1)]['code']])) + ) { + $multiLineComment = true; + } + + if ($multiLineComment === true) { + // This is the subsequent line of a multi-line comment. Account for indentation. + $commentOnly = \ltrim($origContent); + if ($commentOnly === '' || \strpos($commentOnly, "\t") === false) { + continue; + } + } + + $fix = $phpcsFile->addFixableError( + 'Spaces must be used for mid-line alignment; tabs are not allowed', + $i, + 'NonIndentTabsUsed' + ); + + if ($fix === false) { + continue; + } + + $indent = ''; + if ($multiLineComment === true) { + // Take the original indent (tabs/spaces) and combine with the tab-replaced comment content. + $indent = \str_replace($commentOnly, '', $origContent); + $token['content'] = \ltrim($token['content']); + } + + $phpcsFile->fixer->replaceToken($i, $indent . $token['content']); + } + + // Scanned the whole file in one go. Don't scan this file again. + return $phpcsFile->numTokens; + } +} diff --git a/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/WhiteSpace/PrecisionAlignmentSniff.php b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/WhiteSpace/PrecisionAlignmentSniff.php new file mode 100644 index 0000000..af68968 --- /dev/null +++ b/vendor/phpcsstandards/phpcsextra/Universal/Sniffs/WhiteSpace/PrecisionAlignmentSniff.php @@ -0,0 +1,445 @@ + + * + * + * + * + * + * + * + * + * + * ``` + * + * @since 1.0.0 + * + * @var string[] + */ + public $ignoreAlignmentBefore = []; + + /** + * Whether or not potential trailing whitespace on otherwise blank lines should be examined or ignored. + * + * Defaults to `true`, i.e. ignore blank lines. + * + * It is recommended to only set this to `false` if the standard including this sniff does not + * include the `Squiz.WhiteSpace.SuperfluousWhitespace` sniff (which is included in most standards). + * + * @since 1.0.0 + * + * @var bool + */ + public $ignoreBlankLines = true; + + /** + * The --tab-width CLI value that is being used. + * + * @since 1.0.0 + * + * @var int + */ + private $tabWidth; + + /** + * Whitespace tokens and tokens which can contain leading whitespace. + * + * A few additional tokens will be added to this list in the register() method. + * + * @since 1.0.0 + * + * @var array + */ + private $tokensToCheck = [ + \T_WHITESPACE => \T_WHITESPACE, + \T_INLINE_HTML => \T_INLINE_HTML, + \T_DOC_COMMENT_WHITESPACE => \T_DOC_COMMENT_WHITESPACE, + \T_COMMENT => \T_COMMENT, + \T_END_HEREDOC => \T_END_HEREDOC, + \T_END_NOWDOC => \T_END_NOWDOC, + ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.0.0 + * + * @return array + */ + public function register() + { + // Add the ignore annotation tokens to the list of tokens to check. + $this->tokensToCheck += Tokens::$phpcsCommentTokens; + + return Collections::phpOpenTags(); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return int Integer stack pointer to skip the rest of the file. + */ + public function process(File $phpcsFile, $stackPtr) + { + /* + * Handle the properties. + */ + if (isset($this->tabWidth) === false || \defined('PHP_CODESNIFFER_IN_TESTS') === true) { + $this->tabWidth = Helper::getTabWidth($phpcsFile); + } + + if (isset($this->indent) === true) { + $indent = (int) $this->indent; + } else { + $indent = $this->tabWidth; + } + + $ignoreTokens = (array) $this->ignoreAlignmentBefore; + if (empty($ignoreTokens) === false) { + $ignoreTokens = \array_flip($ignoreTokens); + } + + /* + * Check the whole file in one go. + */ + $tokens = $phpcsFile->getTokens(); + + for ($i = 0; $i < $phpcsFile->numTokens; $i++) { + if ($tokens[$i]['column'] !== 1) { + // Only interested in the first token on each line. + continue; + } + + if (isset($this->tokensToCheck[$tokens[$i]['code']]) === false) { + // Not one of the target tokens. + continue; + } + + if ($tokens[$i]['content'] === $phpcsFile->eolChar) { + // Skip completely blank lines. + continue; + } + + if (isset($ignoreTokens[$tokens[$i]['type']]) === true + || (isset($tokens[($i + 1)]) && isset($ignoreTokens[$tokens[($i + 1)]['type']])) + ) { + // This is one of the tokens being ignored. + continue; + } + + $origContent = null; + if (isset($tokens[$i]['orig_content']) === true) { + $origContent = $tokens[$i]['orig_content']; + } + + $spaces = 0; + $length = 0; + $content = ''; + $closer = ''; + + switch ($tokens[$i]['code']) { + case \T_WHITESPACE: + if ($this->ignoreBlankLines === true + && isset($tokens[($i + 1)]) + && $tokens[$i]['line'] !== $tokens[($i + 1)]['line'] + ) { + // Skip blank lines which only contain trailing whitespace. + continue 2; + } + + $spaces = ($tokens[$i]['length'] % $indent); + break; + + case \T_DOC_COMMENT_WHITESPACE: + /* + * Blank lines with trailing whitespace in docblocks are tokenized as + * two T_DOC_COMMENT_WHITESPACE tokens: one for the trailing whitespace, + * one for the new line character. + */ + if ($this->ignoreBlankLines === true + && isset($tokens[($i + 1)]) + && $tokens[($i + 1)]['content'] === $phpcsFile->eolChar + && isset($tokens[($i + 2)]) + && $tokens[$i]['line'] !== $tokens[($i + 2)]['line'] + ) { + // Skip blank lines which only contain trailing whitespace. + continue 2; + } + + $spaces = ($tokens[$i]['length'] % $indent); + + if (isset($tokens[($i + 1)]) === true + && ($tokens[($i + 1)]['code'] === \T_DOC_COMMENT_STAR + || $tokens[($i + 1)]['code'] === \T_DOC_COMMENT_CLOSE_TAG) + && $spaces !== 0 + ) { + // One alignment space expected before the *. + --$spaces; + } + break; + + case \T_COMMENT: + case \T_INLINE_HTML: + if ($this->ignoreBlankLines === true + && \trim($tokens[$i]['content']) === '' + && isset($tokens[($i + 1)]) + && $tokens[$i]['line'] !== $tokens[($i + 1)]['line'] + ) { + // Skip blank lines which only contain trailing whitespace. + continue 2; + } + + // Deliberate fall-through. + + case \T_PHPCS_ENABLE: + case \T_PHPCS_DISABLE: + case \T_PHPCS_SET: + case \T_PHPCS_IGNORE: + case \T_PHPCS_IGNORE_FILE: + /* + * Indentation is included in the contents of the token for: + * - inline HTML + * - PHP 7.3+ flexible heredoc/nowdoc closer identifiers (see below); + * - subsequent lines of multi-line comments; + * - PHPCS native annotations when part of a multi-line comment. + */ + $content = \ltrim($tokens[$i]['content']); + $whitespace = \str_replace($content, '', $tokens[$i]['content']); + + /* + * If there is no content, this is a blank line in a comment or in inline HTML. + * In that case, use the predetermined length as otherwise the new line character + * at the end of the whitespace will throw the count off. + */ + $length = ($content === '') ? $tokens[$i]['length'] : \strlen($whitespace); + $spaces = ($length % $indent); + + /* + * For multi-line star-comments, which use (aligned) stars on subsequent + * lines, we don't want to trigger on the one extra space before the star. + * + * While not 100% correct, don't exclude inline HTML from this check as + * otherwise the sniff would trigger on multi-line /*-style inline javascript comments. + * This may cause false negatives as there is no check for being in a + *