diff --git a/.github/workflows/php-compat.yml b/.github/workflows/php-compat.yml index f7c3cd58..a5d05804 100644 --- a/.github/workflows/php-compat.yml +++ b/.github/workflows/php-compat.yml @@ -4,35 +4,53 @@ on: pull_request jobs: php-compatibility: + name: PHP compatibility runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Setup proper PHP version - uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0 + - name: Prepare PHP + uses: woocommerce/grow/prepare-php@da5279aa4d76745ef32970e49b5ef7903a3b457a # v2.2.2 with: - php-version: 7.4 - tools: composer - - - name: Validate composer.json and composer.lock - run: composer validate --strict --no-check-all - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + tools: cs2pr + install-deps: no - - name: Cache dependencies - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + - name: Prepare node + uses: woocommerce/grow/prepare-node@da5279aa4d76745ef32970e49b5ef7903a3b457a # v2.2.2 with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-composer- - - - name: Install dependencies - run: composer install - - - name: Run PHP Compatibility - run: ./vendor/bin/phpcs --standard=phpcs-compat.xml.dist -p . + node-version-file: ".nvmrc" + ignore-scripts: "no" + + # The use of WP-Scripts results in PHP files being generated during the npm build step. + # In order to ensure PHP Compatibility, the build step must be run in order to ensure all + # files in the production release of the plugin are compatible with the target PHP versions. + - name: Build production bundle + run: npm run build + + # Unzip the newly created zip file to a separate directory. + - name: Unzip production bundle + run: | + mv woocommerce-square.zip ~ + cd ~ + mkdir prod + unzip woocommerce-square.zip -d prod + cd - + + - name: Copy phpcompat sniffs to production directory + run: cp phpcs-compat.xml.dist ~/prod/woocommerce-square/phpcs-compat.xml.dist + + # Installed globally to ensure dev dependencies are not included in compatibility sniffs. + # At the time of writing no production version of PHPCompatibility/PHP-Compatibility supports + # PHP 8.4 so a dev version is used in order to ensure the sniffs that have been created are + # included in the checks. + - name: Install PHP Compatibility dev-develop globally + run: | + composer global config --no-plugins allow-plugins.dealerdirect/phpcodesniffer-composer-installer true + composer global require --dev phpcompatibility/php-compatibility:dev-develop#8daeec54772a592ad369be23ae02ed593c71e7f1 + + - name: Run PHPCompatibility on all files + run: | + cd ~/prod/woocommerce-square + ~/.composer/vendor/bin/phpcs . --standard=phpcs-compat.xml.dist -ps --basepath=. diff --git a/composer.json b/composer.json index 79c5d3c0..78d5e6a1 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,12 @@ "!/vendor", "!/assets/js/**/*.min.js", "!/assets/css/**/*.min.css", - "*.zip" + "*.zip", + "/vendor/apimatic/*/tests/**", + "/vendor/apimatic/*/.*", + "/vendor/square/square/tests/**", + "/vendor/square/square/doc/**", + "/vendor/square/square/.*" ] }, "extra": { diff --git a/includes/Admin/Rest/WC_REST_Square_Settings_Controller.php b/includes/Admin/Rest/WC_REST_Square_Settings_Controller.php index 39f40980..41899cf4 100644 --- a/includes/Admin/Rest/WC_REST_Square_Settings_Controller.php +++ b/includes/Admin/Rest/WC_REST_Square_Settings_Controller.php @@ -179,7 +179,7 @@ public function get_settings() { $filtered_settings['connection_url'] = wc_square()->get_gateway()->get_plugin()->get_connection_handler()->get_connect_url( false ); $filtered_settings['connection_url_wizard'] = wc_square()->get_gateway()->get_plugin()->get_connection_handler()->get_connect_url( false, array( 'from' => 'wizard' ) ); $filtered_settings['connection_url_sandbox'] = wc_square()->get_gateway()->get_plugin()->get_connection_handler()->get_connect_url( true, array( 'from' => 'wizard' ) ); - $filtered_settings['disconnection_url'] = html_entity_decode( wp_nonce_url( $url, $action ) ); + $filtered_settings['disconnection_url'] = html_entity_decode( wp_nonce_url( $url, $action ), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ); // Add locations to the response. if ( wc_square()->get_settings_handler()->is_connected() ) { diff --git a/includes/Framework/PaymentGateway/Admin/Payment_Gateway_Admin_Order.php b/includes/Framework/PaymentGateway/Admin/Payment_Gateway_Admin_Order.php index 24ae335a..bf8e9011 100644 --- a/includes/Framework/PaymentGateway/Admin/Payment_Gateway_Admin_Order.php +++ b/includes/Framework/PaymentGateway/Admin/Payment_Gateway_Admin_Order.php @@ -462,7 +462,7 @@ public function ajax_process_capture() { wp_send_json_success( array( - 'message' => html_entity_decode( wp_strip_all_tags( $result['message'] ) ), // ensure any HTML tags are removed and the currency symbol entity is decoded + 'message' => html_entity_decode( wp_strip_all_tags( $result['message'] ), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ), // ensure any HTML tags are removed and the currency symbol entity is decoded ) ); diff --git a/includes/Framework/PaymentGateway/Payment_Gateway.php b/includes/Framework/PaymentGateway/Payment_Gateway.php index 266ba081..1651c02f 100644 --- a/includes/Framework/PaymentGateway/Payment_Gateway.php +++ b/includes/Framework/PaymentGateway/Payment_Gateway.php @@ -3621,7 +3621,7 @@ public function get_authorization_time_window() { * @param \WC_Order $order Optional. The order being charged * @return bool */ - public function perform_credit_card_charge( \WC_Order $order = null ) { + public function perform_credit_card_charge( ?\WC_Order $order = null ) { assert( $this->supports_credit_card_charge() ); @@ -3652,7 +3652,7 @@ public function perform_credit_card_charge( \WC_Order $order = null ) { * @param \WC_Order $order Optional. The order being authorized * @return bool */ - public function perform_credit_card_authorization( \WC_Order $order = null ) { + public function perform_credit_card_authorization( ?\WC_Order $order = null ) { assert( $this->supports_credit_card_authorization() ); @@ -3677,7 +3677,7 @@ public function perform_credit_card_authorization( \WC_Order $order = null ) { * @param \WC_Order $order Optional. The order being charged * @return bool */ - public function perform_charge( \WC_Order $order = null ) { + public function perform_charge( ?\WC_Order $order = null ) { assert( $this->supports_charge() ); @@ -3708,7 +3708,7 @@ public function perform_charge( \WC_Order $order = null ) { * @param \WC_Order $order Optional. The order being authorized * @return bool */ - public function perform_authorization( \WC_Order $order = null ) { + public function perform_authorization( ?\WC_Order $order = null ) { assert( $this->supports_authorization() ); @@ -4002,12 +4002,12 @@ public function add_debug_message( $message, $type = 'message' ) { if ( 'message' === $type ) { - Square_Helper::wc_add_notice( str_replace( "\n", '
', htmlspecialchars( $message ) ), 'notice' ); + Square_Helper::wc_add_notice( str_replace( "\n", '
', htmlspecialchars( $message, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ) ), 'notice' ); } else { // defaults to error message - Square_Helper::wc_add_notice( str_replace( "\n", '
', htmlspecialchars( $message ) ), 'error' ); + Square_Helper::wc_add_notice( str_replace( "\n", '
', htmlspecialchars( $message, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ) ), 'error' ); } } } diff --git a/includes/Framework/Square_Helper.php b/includes/Framework/Square_Helper.php index d4c99380..40c1adaf 100644 --- a/includes/Framework/Square_Helper.php +++ b/includes/Framework/Square_Helper.php @@ -98,11 +98,14 @@ public static function str_exists( $haystack, $needle ) { */ public static function str_to_ascii( $string ) { - // strip ASCII chars 32 and under - $string = filter_var( $string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW ); + // Remove HTML tags. + $string = wp_strip_all_tags( $string ); - // strip ASCII chars 127 and higher - return filter_var( $string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH ); + // Encode HTML special characters. + $string = htmlspecialchars( $string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ); + + // strip ASCII chars 32 and under; 127 and higher + return filter_var( $string, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH ); } /** diff --git a/includes/Gateway/API.php b/includes/Gateway/API.php index 680f0ed1..1012b11e 100644 --- a/includes/Gateway/API.php +++ b/includes/Gateway/API.php @@ -731,7 +731,7 @@ public function retrieve_gift_card_by_gan( $gan = '' ) { * * @throws \Exception */ - public function search_orders( $location_ids = array(), $start_time, $limit = 100, $cursor = '', $end_time = '' ) { + public function search_orders( $location_ids, $start_time, $limit = 100, $cursor = '', $end_time = '' ) { $request = new API\Requests\Orders( $this->client ); diff --git a/includes/Handlers/Product.php b/includes/Handlers/Product.php index 4fbf1bd5..ee7370ba 100644 --- a/includes/Handlers/Product.php +++ b/includes/Handlers/Product.php @@ -1012,7 +1012,7 @@ public static function extract_catalog_item_data( \WC_Product $product, array $v * * @param bool $is_soft_delete whether or not this item data is for a soft-delete * @return array */ - public static function extract_catalog_item_variation_data( \WC_Product $product, \WC_Product $parent_product = null, $is_soft_delete = false ) { + public static function extract_catalog_item_variation_data( \WC_Product $product, ?\WC_Product $parent_product = null, $is_soft_delete = false ) { if ( ! $product ) { return null; diff --git a/includes/Sync/Catalog_Item.php b/includes/Sync/Catalog_Item.php index b21d346a..b14bb73c 100644 --- a/includes/Sync/Catalog_Item.php +++ b/includes/Sync/Catalog_Item.php @@ -78,7 +78,7 @@ public function __construct( $product, $is_soft_delete = false ) { * @return \Square\Models\CatalogObjectBatch * @throws \Exception */ - public function get_batch( \Square\Models\CatalogObject $catalog_object = null ) { + public function get_batch( ?\Square\Models\CatalogObject $catalog_object = null ) { if ( ! $this->batch ) { $this->create_batch( $catalog_object ); @@ -122,7 +122,7 @@ protected function is_soft_delete() { * @param \Square\Models\CatalogObject|null $catalog_object existing catalog object or null to create a new one * @throws \Exception */ - protected function create_batch( \Square\Models\CatalogObject $catalog_object = null ) { + protected function create_batch( ?\Square\Models\CatalogObject $catalog_object = null ) { if ( ! $catalog_object ) { $catalog_id = Product\Woo_SOR::get_square_item_id( $this->product ); diff --git a/includes/Utilities/String_Utility.php b/includes/Utilities/String_Utility.php index a4aa0851..dbba4fd5 100644 --- a/includes/Utilities/String_Utility.php +++ b/includes/Utilities/String_Utility.php @@ -78,18 +78,21 @@ public static function truncate( $string, $length, $omission = '...' ) { * @return string */ public static function to_ascii( $string ) { - // strip ASCII chars 32 and under - $string = filter_var( $string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW ); + // Remove HTML tags. + $string = wp_strip_all_tags( $string ); - // strip ASCII chars 127 and higher - return filter_var( $string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH ); + // Encode HTML special characters. + $string = htmlspecialchars( $string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ); + + // strip ASCII chars 32 and under; 127 and higher + return filter_var( $string, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH ); } /** * Returns true if the haystack string starts with needle * * Note: case-sensitive - * + * * See Square_Helper::str_starts_with() * * @since 2.2.0 diff --git a/phpcs-compat.xml.dist b/phpcs-compat.xml.dist index 8a4a8899..51a6d42e 100644 --- a/phpcs-compat.xml.dist +++ b/phpcs-compat.xml.dist @@ -1,19 +1,65 @@ - - + - - - . + WordPress specific ruleset which checks for PHP cross version compatibility. + - vendor/ - dist/ - docker/ - build/ - docs/ - node_modules - tests - assets + + + . + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/woocommerce-square.php b/woocommerce-square.php index 682d9560..e0194258 100644 --- a/woocommerce-square.php +++ b/woocommerce-square.php @@ -7,7 +7,7 @@ * Requires at least: 6.7 * Tested up to: 6.8 * Requires PHP: 7.4 - * PHP tested up to: 8.3 + * PHP tested up to: 8.4 * * Description: Securely accept payments, synchronize sales, and seamlessly manage inventory and product data between WooCommerce and Square POS. * Author: WooCommerce