From 8adf758f28ad890c200dbb1c1d41c77f79188b77 Mon Sep 17 00:00:00 2001 From: Chauncey McAskill Date: Wed, 15 Mar 2023 11:24:57 -0400 Subject: [PATCH 1/3] Add EditorConfig ruleset and fix coding styles Enforces minimal coding styles to match existing coding styles inspired by WordPress' own styles. Changed: - Replaced inconsistent indentation in `RemoteFilesystem`. - Removed colon in pull request template to match other titles. --- .editorconfig | 21 ++ .github/PULL_REQUEST_TEMPLATE.md | 20 +- LICENSE | 2 + README.md | 476 ++++++++++++++++--------------- composer.json | 46 +-- src/RemoteFilesystem.php | 86 +++--- 6 files changed, 342 insertions(+), 309 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..41d203a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +# WordPress Coding Standards +# https://make.wordpress.org/core/handbook/coding-standards/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = tab +insert_final_newline = true +trim_trailing_whitespace = true + +[*.yml] +indent_size = 2 +indent_style = space + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ea3ef9d..63f9c8b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,21 +1,27 @@ -## Checklist: -- [ ] I've read the [Contributing page](https://github.com/junaidbhura/composer-wp-pro-plugins/blob/master/CONTRIBUTING.md). -- [ ] I've created an issue and referenced it here. -- [ ] My code is tested. -- [ ] My code follows the WordPress code style. -- [ ] My code has proper inline documentation. +## Checklist + +* [ ] I've read the [Contributing page](https://github.com/junaidbhura/composer-wp-pro-plugins/blob/master/CONTRIBUTING.md). +* [ ] I've created an issue and referenced it here. +* [ ] My code is tested. +* [ ] My code follows the WordPress code style. +* [ ] My code has proper inline documentation. ## Description + ## How has this been tested? + -## Screenshots +## Screenshots + + ## Types of changes + diff --git a/LICENSE b/LICENSE index d6cc2c2..6f56881 100644 --- a/LICENSE +++ b/LICENSE @@ -2,6 +2,8 @@ MIT License Copyright (c) 2019 Junaid Bhura +Copyright (c) 2024 Chauncey McAskill + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights diff --git a/README.md b/README.md index d2634ed..d69191f 100644 --- a/README.md +++ b/README.md @@ -17,27 +17,31 @@ Sensitive credentials (license keys, tokens) are read from environment variables ## Overview -> ⚠️ Note: Most EDD plugins, and Gravity Forms, only allow downloading the latest versions of their plugins, even if you request for a specific version. +> ⚠️ Note: Most EDD plugins, and Gravity Forms, only allow downloading the latest versions +> of their plugins, even if you request for a specific version. -- Packages must use the names defined below otherwise they are ignored by this plugin. -- When installing or updating a package, the package version is appended to the dist URL. +* Packages must use the names defined below otherwise they are ignored by this plugin. +* When installing or updating a package, the package version is appended to the dist URL. This versioned dist URL is used as the cache key to store ZIP archives of the package. In Composer 1, the versioned dist URL is added to `composer.lock`. -- Before downloading the package, the package's real download URL is retrieved and formatted with their corresponding environment variables, as defined below. +* Before downloading the package, the package's real download URL is retrieved and formatted + with their corresponding environment variables, as defined below. Environment variables will never be stored inside `composer.lock`. -- If an environment variable can't be resolved, the download will fail and Composer will abort. +* If an environment variable can't be resolved, the download will fail and Composer will abort. ## Usage This Composer plugin requires [Composer](https://getcomposer.org/): -- 1.0.0 and newer, or -- 2.0.2 and newer -- 2.3+ IMPORTANT: Make sure to add trailing slashes to packages' `dist` URL as below. More info: https://github.com/junaidbhura/composer-wp-pro-plugins/issues/34 +* 1.0.0 and newer, or +* 2.0.2 and newer +* 2.3+ IMPORTANT: Make sure to add trailing slashes to packages' `dist` URL as below. + More info: https://github.com/junaidbhura/composer-wp-pro-plugins/issues/34 -Create a `.env` file in the root of your WordPress site, where the `composer.json` file lives, which has all the license keys and settings: +Create a `.env` file in the root of your WordPress site, where the `composer.json` file lives, +which has all the license keys and settings: -``` +```shell ACF_PRO_KEY="" ACFE_PRO_KEY="" ACFE_PRO_URL="" @@ -64,233 +68,233 @@ Add the following to your composer.json file: ```json "repositories": [ - { - "type": "package", - "package": { - "name": "junaidbhura/acf-extended-pro", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://www.acf-extended.com/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/advanced-custom-fields-pro", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://www.advancedcustomfields.com/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/gravityforms", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://www.gravityforms.com/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/gravityformspolls", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://www.gravityforms.com/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/ninja-forms-uploads", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://ninjaforms.com/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/polylang-pro", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://www.polylang.pro/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/publishpress-planner-pro", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://publishpress.com/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/wp-all-import-pro", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://www.wpallimport.com/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/wp-all-export-pro", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://www.wpallimport.com/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/wpai-acf-add-on", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://www.wpallimport.com/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/wpae-acf-add-on", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://www.wpallimport.com/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/wpae-user-add-on-pro", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://www.wpallimport.com/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/wpml-sitepress-multilingual-cms", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://wpml.org/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - }, - { - "type": "package", - "package": { - "name": "junaidbhura/wpml-string-translation", - "version": "", - "type": "wordpress-plugin", - "dist": { - "type": "zip", - "url": "https://wpml.org/" - }, - "require": { - "junaidbhura/composer-wp-pro-plugins": "*" - } - } - } + { + "type": "package", + "package": { + "name": "junaidbhura/acf-extended-pro", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://www.acf-extended.com/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/advanced-custom-fields-pro", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://www.advancedcustomfields.com/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/gravityforms", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://www.gravityforms.com/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/gravityformspolls", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://www.gravityforms.com/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/ninja-forms-uploads", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://ninjaforms.com/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/polylang-pro", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://www.polylang.pro/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/publishpress-planner-pro", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://publishpress.com/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/wp-all-import-pro", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://www.wpallimport.com/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/wp-all-export-pro", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://www.wpallimport.com/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/wpai-acf-add-on", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://www.wpallimport.com/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/wpae-acf-add-on", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://www.wpallimport.com/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/wpae-user-add-on-pro", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://www.wpallimport.com/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/wpml-sitepress-multilingual-cms", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://wpml.org/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + }, + { + "type": "package", + "package": { + "name": "junaidbhura/wpml-string-translation", + "version": "", + "type": "wordpress-plugin", + "dist": { + "type": "zip", + "url": "https://wpml.org/" + }, + "require": { + "junaidbhura/composer-wp-pro-plugins": "*" + } + } + } ], "require": { - "junaidbhura/acf-extended-pro": "*", - "junaidbhura/advanced-custom-fields-pro": "*", - "junaidbhura/gravityforms": "*", - "junaidbhura/gravityformspolls": "*", - "junaidbhura/ninja-forms-uploads": "*", - "junaidbhura/polylang-pro": "*", - "junaidbhura/publishpress-planner-pro": "*", - "junaidbhura/wp-all-import-pro": "*", - "junaidbhura/wp-all-export-pro": "*", - "junaidbhura/wpai-acf-add-on": "*", - "junaidbhura/wpae-acf-add-on": "*", - "junaidbhura/wpae-user-add-on-pro": "*", - "junaidbhura/wpml-sitepress-multilingual-cms": "*", - "junaidbhura/wpml-string-translation": "*" -}, + "junaidbhura/acf-extended-pro": "*", + "junaidbhura/advanced-custom-fields-pro": "*", + "junaidbhura/gravityforms": "*", + "junaidbhura/gravityformspolls": "*", + "junaidbhura/ninja-forms-uploads": "*", + "junaidbhura/polylang-pro": "*", + "junaidbhura/publishpress-planner-pro": "*", + "junaidbhura/wp-all-import-pro": "*", + "junaidbhura/wp-all-export-pro": "*", + "junaidbhura/wpai-acf-add-on": "*", + "junaidbhura/wpae-acf-add-on": "*", + "junaidbhura/wpae-user-add-on-pro": "*", + "junaidbhura/wpml-sitepress-multilingual-cms": "*", + "junaidbhura/wpml-string-translation": "*" +} ``` ### Gravity Forms Add-Ons @@ -303,7 +307,7 @@ For example: `junaidbhura/gravityformspolls` -Here's a list of all Gravity Forms add-on slugs: [https://docs.gravityforms.com/gravity-forms-add-on-slugs/](https://docs.gravityforms.com/gravity-forms-add-on-slugs/) +Consult the [list of all Gravity Forms add-on slugs](https://docs.gravityforms.com/gravity-forms-add-on-slugs/) ### Ninja Forms Add-Ons diff --git a/composer.json b/composer.json index 666551e..a755a37 100644 --- a/composer.json +++ b/composer.json @@ -1,25 +1,25 @@ { - "name": "junaidbhura/composer-wp-pro-plugins", - "type": "composer-plugin", - "license": "MIT", - "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "composer/semver": "^1.0 || ^2.0 || ^3.0", - "vlucas/phpdotenv": "^3.0 || ^4.0 || ^5.0" - }, - "authors": [ - { - "name": "Junaid Bhura", - "email": "info@junaidbhura.com" - } - ], - "autoload": { - "psr-4": { - "Junaidbhura\\Composer\\WPProPlugins\\": "src/" - } - }, - "extra": { - "class": "Junaidbhura\\Composer\\WPProPlugins\\Installer", - "plugin-modifies-downloads": true - } + "name": "junaidbhura/composer-wp-pro-plugins", + "type": "composer-plugin", + "license": "MIT", + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "composer/semver": "^1.0 || ^2.0 || ^3.0", + "vlucas/phpdotenv": "^3.0 || ^4.0 || ^5.0" + }, + "authors": [ + { + "name": "Junaid Bhura", + "email": "info@junaidbhura.com" + } + ], + "autoload": { + "psr-4": { + "Junaidbhura\\Composer\\WPProPlugins\\": "src/" + } + }, + "extra": { + "class": "Junaidbhura\\Composer\\WPProPlugins\\Installer", + "plugin-modifies-downloads": true + } } diff --git a/src/RemoteFilesystem.php b/src/RemoteFilesystem.php index c05468a..c74890e 100644 --- a/src/RemoteFilesystem.php +++ b/src/RemoteFilesystem.php @@ -15,34 +15,34 @@ */ class RemoteFilesystem extends \Composer\Util\RemoteFilesystem { - /** - * Override original fileUrl. - * - * @access protected - * @var string - */ - protected $fileUrl; + /** + * Override original fileUrl. + * + * @access protected + * @var string + */ + protected $fileUrl; - /** - * Constructor. - * - * @access public - * @param string $fileUrl The url that should be used instead of fileurl - * @param IOInterface $io The IO instance - * @param Config $config The config - * @param array $options The options - * @param bool $disableTls - */ - public function __construct( - $fileUrl, - IOInterface $io, - Config $config = null, - array $options = [], - $disableTls = false - ) { - $this->fileUrl = $fileUrl; - parent::__construct( $io, $config, $options, $disableTls ); - } + /** + * Constructor. + * + * @access public + * @param string $fileUrl The url that should be used instead of fileurl + * @param IOInterface $io The IO instance + * @param Config $config The config + * @param array $options The options + * @param bool $disableTls + */ + public function __construct( + $fileUrl, + IOInterface $io, + Config $config = null, + array $options = [], + $disableTls = false + ) { + $this->fileUrl = $fileUrl; + parent::__construct( $io, $config, $options, $disableTls ); + } /** * Copy the remote file to local. @@ -53,21 +53,21 @@ public function __construct( * @param bool $progress Display the progression * @param array $options Additional context options * @return bool true - */ - public function copy( - $originUrl, - $fileUrl, - $fileName, - $progress = true, - $options = [] - ) { - return parent::copy( - $originUrl, - $this->fileUrl, - $fileName, - $progress, - $options - ); - } + */ + public function copy( + $originUrl, + $fileUrl, + $fileName, + $progress = true, + $options = [] + ) { + return parent::copy( + $originUrl, + $this->fileUrl, + $fileName, + $progress, + $options + ); + } } From 65041dd141ea94a81250b408e16d93af2efb5eef Mon Sep 17 00:00:00 2001 From: Chauncey McAskill Date: Wed, 15 Mar 2023 11:53:27 -0400 Subject: [PATCH 2/3] Require PHPStan and fix reported issues Required: - composer/composer ^1.0 or ^2.0 - phpstan/phpstan ^1.10 Usage: ```shell composer lint:phpstan -- --xdebug ``` --- .gitignore | 1 + composer.json | 13 ++++++++++++ phpstan.neon.dist | 4 ++++ src/Http.php | 11 +++++++++++ src/Installer.php | 33 ++++++++++++++++++++----------- src/Plugins/AbstractEddPlugin.php | 8 +++----- src/Plugins/GravityForms.php | 12 +++++------ src/Plugins/NinjaForms.php | 12 +++++------ src/Plugins/PublishPressPro.php | 12 +++++------ src/RemoteFilesystem.php | 26 +++++++++++------------- 10 files changed, 81 insertions(+), 51 deletions(-) create mode 100644 phpstan.neon.dist diff --git a/.gitignore b/.gitignore index 82cfc4e..b22ba49 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea composer.lock +phpstan.neon vendor diff --git a/composer.json b/composer.json index a755a37..5623ffd 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,10 @@ "composer/semver": "^1.0 || ^2.0 || ^3.0", "vlucas/phpdotenv": "^3.0 || ^4.0 || ^5.0" }, + "require-dev": { + "composer/composer": "^1.0.0 || ^2.0.0", + "phpstan/phpstan": "^1.10" + }, "authors": [ { "name": "Junaid Bhura", @@ -21,5 +25,14 @@ "extra": { "class": "Junaidbhura\\Composer\\WPProPlugins\\Installer", "plugin-modifies-downloads": true + }, + "scripts": { + "lint": [ + "@lint:phpstan" + ], + "lint:phpstan": "phpstan analyse" + }, + "scripts-descriptions": { + "lint:phpstan": "Run static code analysis with PHPStan" } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..309bd4f --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,4 @@ +parameters: + level: max + paths: + - src diff --git a/src/Http.php b/src/Http.php index 93bbdfd..c595357 100644 --- a/src/Http.php +++ b/src/Http.php @@ -62,6 +62,11 @@ protected function request( $curl_handle ) { curl_setopt( $curl_handle, CURLOPT_FOLLOWLOCATION, true ); curl_setopt( $curl_handle, CURLOPT_FAILONERROR, true ); + /** + * @var string|false Because the CURLOPT_RETURNTRANSFER option is set, + * the {@see \curl_exec()} function returns the result on success, + * false on failure. + */ $response = curl_exec( $curl_handle ); $curl_errno = curl_errno( $curl_handle ); @@ -76,6 +81,12 @@ protected function request( $curl_handle ) { ), $curl_errno ); } + if ( 0 === strlen( $response ) ) { + throw new RuntimeException( + 'cURL error: Empty response body' + ); + } + return $response; } diff --git a/src/Installer.php b/src/Installer.php index 2a1b25f..341bf87 100644 --- a/src/Installer.php +++ b/src/Installer.php @@ -44,13 +44,14 @@ class Installer implements PluginInterface, EventSubscriberInterface { * * @param Composer $composer * @param IOInterface $io + * @return void */ public function activate( Composer $composer, IOInterface $io ) { $this->composer = $composer; $this->io = $io; $path = getcwd(); - if ( file_exists( $path . DIRECTORY_SEPARATOR . '.env' ) ) { + if ( is_string( $path ) && file_exists( $path . DIRECTORY_SEPARATOR . '.env' ) ) { $this->loadDotenv( $path ); } } @@ -60,10 +61,10 @@ public function activate( Composer $composer, IOInterface $io ) { * * @param Composer $composer * @param IOInterface $io + * @return void */ public function deactivate( Composer $composer, IOInterface $io ) { - $this->composer = null; - $this->io = null; + // no need to deactivate anything } /** @@ -71,6 +72,7 @@ public function deactivate( Composer $composer, IOInterface $io ) { * * @param Composer $composer * @param IOInterface $io + * @return void */ public function uninstall( Composer $composer, IOInterface $io ) { // no need to uninstall anything @@ -80,6 +82,7 @@ public function uninstall( Composer $composer, IOInterface $io ) { * Activate vlucas/phpdotenv, if available. * * @param string $path + * @return void */ protected function loadDotenv( $path ) { // Dotenv V5 @@ -107,10 +110,9 @@ protected function loadDotenv( $path ) { /** * Set subscribed events. * - * @return array + * @return array */ public static function getSubscribedEvents() { - if ( version_compare( PluginInterface::PLUGIN_API_VERSION, '2.0.0', '<' ) ) { return array( PackageEvents::PRE_PACKAGE_INSTALL => 'onPrePackageInstallOrUpdateInComposer1', @@ -133,6 +135,7 @@ public static function getSubscribedEvents() { * the package as a context. * * @param PackageEvent $event + * @return void */ public function onPrePackageInstallOrUpdateInComposer1( PackageEvent $event ) { $this->downloadUrl = null; @@ -148,9 +151,11 @@ public function onPrePackageInstallOrUpdateInComposer1( PackageEvent $event ) { if ( ! empty( $download_url ) ) { $this->downloadUrl = $download_url; - $dist_url = $package->getDistUrl(); - $filtered_url = $this->filterDistUrl( $dist_url, $package ); - $package->setDistUrl( $filtered_url ); + $dist_url = $package->getDistUrl(); + if ( is_string( $dist_url ) ) { + $filtered_url = $this->filterDistUrl( $dist_url, $package ); + $package->setDistUrl( $filtered_url ); + } } } @@ -160,6 +165,7 @@ public function onPrePackageInstallOrUpdateInComposer1( PackageEvent $event ) { * In Composer v1, packages are downloaded, installed/updated, sequentially. * * @param PreFileDownloadEvent $event + * @return void */ public function onPreFileDownloadInComposer1( PreFileDownloadEvent $event ) { if ( empty( $this->downloadUrl ) ) { @@ -184,6 +190,7 @@ public function onPreFileDownloadInComposer1( PreFileDownloadEvent $event ) { * then prepared, then installed/updated. * * @param PreFileDownloadEvent $event + * @return void */ public function onPreFileDownloadInComposer2( PreFileDownloadEvent $event ) { /** @@ -195,7 +202,11 @@ public function onPreFileDownloadInComposer2( PreFileDownloadEvent $event ) { return; } - $package = $event->getContext(); + $package = $event->getContext(); + if ( ! $package instanceof PackageInterface ) { + return; + } + $processed_url = $event->getProcessedUrl(); $download_url = $this->getDownloadUrl( $package ); @@ -214,10 +225,10 @@ public function onPreFileDownloadInComposer2( PreFileDownloadEvent $event ) { * The filtered dist URL is stored inside `composer.lock` and is used * to generate the cache key for the requested package version. * - * @param string|null $url + * @param string $url * @param PackageInterface $package * - * @return string|null The filtered dist URL. + * @return string The filtered dist URL. */ protected function filterDistUrl( $url, PackageInterface $package ) { $package_key = sha1( $package->getUniqueName() ); diff --git a/src/Plugins/AbstractEddPlugin.php b/src/Plugins/AbstractEddPlugin.php index d575b3c..2e32fc7 100644 --- a/src/Plugins/AbstractEddPlugin.php +++ b/src/Plugins/AbstractEddPlugin.php @@ -26,7 +26,7 @@ abstract protected function getDownloadUrlFromApi(); /** * Get the download URL for this plugin. * - * @throws UnexpectedValueException If the response is invalid. + * @throws UnexpectedValueException If the response is invalid or versions do not match. * @return string */ public function getDownloadUrl() { @@ -87,9 +87,7 @@ public function getDownloadUrl() { $this->getPackageName() ); - if ( $details ) { - $message .= PHP_EOL . PHP_EOL . implode( PHP_EOL . PHP_EOL, $details ); - } + $message .= PHP_EOL . PHP_EOL . implode( PHP_EOL . PHP_EOL, $details ); throw new UnexpectedValueException( $message ); } @@ -109,7 +107,7 @@ public function getDownloadUrl() { } // If no version is specified, we are fetching the latest version. - if ( $this->version && ! Semver::satisfies( $data['new_version'], $this->version ) ) { + if ( $this->version && ! Semver::satisfies( (string) $data['new_version'], $this->version ) ) { throw new UnexpectedValueException( sprintf( 'Expected download version from API (%s) to match installed version (%s) of package %s', $data['new_version'], diff --git a/src/Plugins/GravityForms.php b/src/Plugins/GravityForms.php index 082e79c..6ba7d14 100644 --- a/src/Plugins/GravityForms.php +++ b/src/Plugins/GravityForms.php @@ -30,7 +30,7 @@ public function __construct( $version = '', $slug = 'gravityforms' ) { /** * Get the download URL for this plugin. * - * @throws UnexpectedValueException If the response is invalid. + * @throws UnexpectedValueException If the response is invalid or versions do not match. * @return string */ public function getDownloadUrl() { @@ -76,7 +76,7 @@ public function getDownloadUrl() { */ $data = unserialize( $response ); - if ( $data !== false && ! is_array( $data ) ) { + if ( ! is_array( $data ) ) { throw new UnexpectedValueException( 'unserialize(): Expected a data structure' ); @@ -96,9 +96,7 @@ public function getDownloadUrl() { $this->getPackageName() ); - if ( $details ) { - $message .= PHP_EOL . PHP_EOL . implode( PHP_EOL . PHP_EOL, $details ); - } + $message .= PHP_EOL . PHP_EOL . implode( PHP_EOL . PHP_EOL, $details ); throw new UnexpectedValueException( $message ); } @@ -124,7 +122,7 @@ public function getDownloadUrl() { * @param array $response The EDD API response. * @param string $version_key The API field key that holds the version. * @param string $download_key The API field key that holds the download URL. - * @throws UnexpectedValueException If the response is not OK, invalid, or malformed. + * @throws UnexpectedValueException If the response is invalid or versions do not match. * @return string */ protected function findDownloadUrl( array $response, $version_key, $download_key ) { @@ -153,7 +151,7 @@ protected function findDownloadUrl( array $response, $version_key, $download_key ) ); } - if ( ! Semver::satisfies( $version, $this->version ) ) { + if ( ! Semver::satisfies( (string) $version, $this->version ) ) { throw new UnexpectedValueException( sprintf( 'Expected download version from API (%s) to match installed version (%s) of package %s.', $version, diff --git a/src/Plugins/NinjaForms.php b/src/Plugins/NinjaForms.php index 4b95b74..c473d7a 100644 --- a/src/Plugins/NinjaForms.php +++ b/src/Plugins/NinjaForms.php @@ -303,13 +303,11 @@ protected function getDownloadUrlFromApi() { ) ); } - if ( $env ) { - /** - * Use add-on licensing if available, otherwise use membership licensing. - */ - $license = ( getenv( "NINJA_FORMS_{$env}_KEY" ) ?: $license ); - $url = ( getenv( "NINJA_FORMS_{$env}_URL" ) ?: $url ); - } + /** + * Use add-on licensing if available, otherwise use membership licensing. + */ + $license = ( getenv( "NINJA_FORMS_{$env}_KEY" ) ?: $license ); + $url = ( getenv( "NINJA_FORMS_{$env}_URL" ) ?: $url ); $http = new Http(); diff --git a/src/Plugins/PublishPressPro.php b/src/Plugins/PublishPressPro.php index 2252383..376c446 100644 --- a/src/Plugins/PublishPressPro.php +++ b/src/Plugins/PublishPressPro.php @@ -96,13 +96,11 @@ protected function getDownloadUrlFromApi() { ) ); } - if ( $env ) { - /** - * Use add-on licensing if available, otherwise use membership licensing. - */ - $license = ( getenv( "PUBLISHPRESS_{$env}_PRO_KEY" ) ?: $license ); - $url = ( getenv( "PUBLISHPRESS_{$env}_PRO_URL" ) ?: $url ); - } + /** + * Use add-on licensing if available, otherwise use membership licensing. + */ + $license = ( getenv( "PUBLISHPRESS_{$env}_PRO_KEY" ) ?: $license ); + $url = ( getenv( "PUBLISHPRESS_{$env}_PRO_URL" ) ?: $url ); $http = new Http(); diff --git a/src/RemoteFilesystem.php b/src/RemoteFilesystem.php index c74890e..8fc61b7 100644 --- a/src/RemoteFilesystem.php +++ b/src/RemoteFilesystem.php @@ -18,25 +18,23 @@ class RemoteFilesystem extends \Composer\Util\RemoteFilesystem { /** * Override original fileUrl. * - * @access protected - * @var string + * @var non-empty-string */ protected $fileUrl; /** * Constructor. * - * @access public - * @param string $fileUrl The url that should be used instead of fileurl - * @param IOInterface $io The IO instance - * @param Config $config The config - * @param array $options The options - * @param bool $disableTls + * @param non-empty-string $fileUrl The url that should be used instead of fileurl + * @param IOInterface $io The IO instance + * @param Config $config The config + * @param array $options The options + * @param bool $disableTls */ public function __construct( $fileUrl, IOInterface $io, - Config $config = null, + Config $config, array $options = [], $disableTls = false ) { @@ -47,11 +45,11 @@ public function __construct( /** * Copy the remote file to local. * - * @param string $originUrl The origin URL - * @param string $fileUrl The file URL (ignored) - * @param string $fileName the local filename - * @param bool $progress Display the progression - * @param array $options Additional context options + * @param string $originUrl The origin URL + * @param non-empty-string $fileUrl The file URL (ignored) + * @param string $fileName the local filename + * @param bool $progress Display the progression + * @param array $options Additional context options * @return bool true */ public function copy( From b81ce0bf0cc664019d2ec1a35b623c108f69a718 Mon Sep 17 00:00:00 2001 From: Chauncey McAskill Date: Wed, 15 Mar 2023 14:04:14 -0400 Subject: [PATCH 3/3] Add conditional baselines for PHPStan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on the PHP version and installed package versions. Added conditionally included baselines for PHP 5–7 and 8+, Composer 1 and 2, Dotenv 3–5. This conditional functionality can be greatly reduced if we drop support for older versions of PHP, Composer, and other dependencies. To resolve these conditional baselines, we have to replace the initial data of `Composer\InstalledVersions` which contains PHPStan's dependencies, since the context is its PHAR. To accomplish this, we have to retrieve the contents of the project's installed dependencies from either: 1. `vendor/composer/installed.php` — Composer v2 PHP format. 2. `vendor/composer/installed.json` — Either Composer v1 or v2 JSON format. The JSON format is tricky because it changes drastically between Composer v1 and v2 and the v2 PHP format. Both JSON formats must be remapped to the PHP format expected by `InstalledVersions`. If the project's installed dependencies cannot be loaded, this file returns only the PHP baselines. If the project's installed dependencies can be loaded, the extra baselines are resolved and the initial data of `InstalledVersions` is restored. --- phpstan.neon.dist | 2 + tests/phpstan/baseline-composer-1.neon | 41 ++++++ tests/phpstan/baseline-composer-2.neon | 31 +++++ tests/phpstan/baseline-php-7.neon | 31 +++++ tests/phpstan/baseline-php-8.neon | 26 ++++ tests/phpstan/baseline-phpdotenv-3.neon | 11 ++ tests/phpstan/baseline-phpdotenv-4.neon | 16 +++ tests/phpstan/baseline-phpdotenv-5.neon | 11 ++ ...nore-errors-by-installed-versions.neon.php | 130 ++++++++++++++++++ 9 files changed, 299 insertions(+) create mode 100644 tests/phpstan/baseline-composer-1.neon create mode 100644 tests/phpstan/baseline-composer-2.neon create mode 100644 tests/phpstan/baseline-php-7.neon create mode 100644 tests/phpstan/baseline-php-8.neon create mode 100644 tests/phpstan/baseline-phpdotenv-3.neon create mode 100644 tests/phpstan/baseline-phpdotenv-4.neon create mode 100644 tests/phpstan/baseline-phpdotenv-5.neon create mode 100644 tests/phpstan/ignore-errors-by-installed-versions.neon.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 309bd4f..cc8f7f1 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,3 +1,5 @@ +includes: + - tests/phpstan/ignore-errors-by-installed-versions.neon.php parameters: level: max paths: diff --git a/tests/phpstan/baseline-composer-1.neon b/tests/phpstan/baseline-composer-1.neon new file mode 100644 index 0000000..581a4c0 --- /dev/null +++ b/tests/phpstan/baseline-composer-1.neon @@ -0,0 +1,41 @@ +parameters: + ignoreErrors: + - + message: "#^Call to an undefined method Composer\\\\Plugin\\\\PreFileDownloadEvent\\:\\:getContext\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Call to an undefined method Composer\\\\Plugin\\\\PreFileDownloadEvent\\:\\:getType\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Call to an undefined method Composer\\\\Plugin\\\\PreFileDownloadEvent\\:\\:setCustomCacheKey\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Call to an undefined method Composer\\\\Plugin\\\\PreFileDownloadEvent\\:\\:setProcessedUrl\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Call to an undefined method Composer\\\\DependencyResolver\\\\Operation\\\\OperationInterface\\:\\:getPackage\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Call to an undefined method Composer\\\\DependencyResolver\\\\Operation\\\\OperationInterface\\:\\:getTargetPackage\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^If condition is always true\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: ../../src/Installer.php diff --git a/tests/phpstan/baseline-composer-2.neon b/tests/phpstan/baseline-composer-2.neon new file mode 100644 index 0000000..e7ef0b9 --- /dev/null +++ b/tests/phpstan/baseline-composer-2.neon @@ -0,0 +1,31 @@ +parameters: + ignoreErrors: + - + message: "#^If condition is always false\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Call to an undefined method Composer\\\\DependencyResolver\\\\Operation\\\\OperationInterface\\:\\:getJobType\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Call to an undefined method Composer\\\\DependencyResolver\\\\Operation\\\\OperationInterface\\:\\:getPackage\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Call to an undefined method Composer\\\\DependencyResolver\\\\Operation\\\\OperationInterface\\:\\:getTargetPackage\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Call to an undefined method Composer\\\\Plugin\\\\PreFileDownloadEvent\\:\\:getRemoteFilesystem\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Call to an undefined method Composer\\\\Plugin\\\\PreFileDownloadEvent\\:\\:setRemoteFilesystem\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php diff --git a/tests/phpstan/baseline-php-7.neon b/tests/phpstan/baseline-php-7.neon new file mode 100644 index 0000000..9e56a50 --- /dev/null +++ b/tests/phpstan/baseline-php-7.neon @@ -0,0 +1,31 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\#1 \\$ch of function curl_close expects resource, CurlHandle\\|resource given\\.$#" + count: 1 + path: ../../src/Http.php + + - + message: "#^Parameter \\#1 \\$ch of function curl_errno expects resource, CurlHandle\\|resource given\\.$#" + count: 1 + path: ../../src/Http.php + + - + message: "#^Parameter \\#1 \\$ch of function curl_error expects resource, CurlHandle\\|resource given\\.$#" + count: 1 + path: ../../src/Http.php + + - + message: "#^Parameter \\#1 \\$ch of function curl_exec expects resource, CurlHandle\\|resource given\\.$#" + count: 1 + path: ../../src/Http.php + + - + message: "#^Parameter \\#1 \\$ch of function curl_setopt expects resource, CurlHandle\\|resource given\\.$#" + count: 3 + path: ../../src/Http.php + + - + message: "#^Parameter \\$curl_handle of method Junaidbhura\\\\Composer\\\\WPProPlugins\\\\Http\\:\\:request\\(\\) has invalid type CurlHandle\\.$#" + count: 1 + path: ../../src/Http.php diff --git a/tests/phpstan/baseline-php-8.neon b/tests/phpstan/baseline-php-8.neon new file mode 100644 index 0000000..bc102f5 --- /dev/null +++ b/tests/phpstan/baseline-php-8.neon @@ -0,0 +1,26 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\#1 \\$handle of function curl_close expects CurlHandle, CurlHandle\\|resource given\\.$#" + count: 1 + path: ../../src/Http.php + + - + message: "#^Parameter \\#1 \\$handle of function curl_errno expects CurlHandle, CurlHandle\\|resource given\\.$#" + count: 1 + path: ../../src/Http.php + + - + message: "#^Parameter \\#1 \\$handle of function curl_error expects CurlHandle, CurlHandle\\|resource given\\.$#" + count: 1 + path: ../../src/Http.php + + - + message: "#^Parameter \\#1 \\$handle of function curl_exec expects CurlHandle, CurlHandle\\|resource given\\.$#" + count: 1 + path: ../../src/Http.php + + - + message: "#^Parameter \\#1 \\$handle of function curl_setopt expects CurlHandle, CurlHandle\\|resource given\\.$#" + count: 3 + path: ../../src/Http.php diff --git a/tests/phpstan/baseline-phpdotenv-3.neon b/tests/phpstan/baseline-phpdotenv-3.neon new file mode 100644 index 0000000..e278d9f --- /dev/null +++ b/tests/phpstan/baseline-phpdotenv-3.neon @@ -0,0 +1,11 @@ +parameters: + ignoreErrors: + - + message: "#^Call to an undefined static method Dotenv\\\\Dotenv\\:\\:createImmutable\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Call to an undefined static method Dotenv\\\\Dotenv\\:\\:createUnsafeImmutable\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php diff --git a/tests/phpstan/baseline-phpdotenv-4.neon b/tests/phpstan/baseline-phpdotenv-4.neon new file mode 100644 index 0000000..d2c44e1 --- /dev/null +++ b/tests/phpstan/baseline-phpdotenv-4.neon @@ -0,0 +1,16 @@ +parameters: + ignoreErrors: + - + message: "#^Call to an undefined static method Dotenv\\\\Dotenv\\:\\:createUnsafeImmutable\\(\\)\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Parameter \\#1 \\$repository of static method Dotenv\\\\Dotenv\\:\\:create\\(\\) expects Dotenv\\\\Repository\\\\RepositoryInterface, string given\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Static method Dotenv\\\\Dotenv\\:\\:create\\(\\) invoked with 1 parameter, 2\\-4 required\\.$#" + count: 1 + path: ../../src/Installer.php diff --git a/tests/phpstan/baseline-phpdotenv-5.neon b/tests/phpstan/baseline-phpdotenv-5.neon new file mode 100644 index 0000000..aa2d44a --- /dev/null +++ b/tests/phpstan/baseline-phpdotenv-5.neon @@ -0,0 +1,11 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\#1 \\$repository of static method Dotenv\\\\Dotenv\\:\\:create\\(\\) expects Dotenv\\\\Repository\\\\RepositoryInterface, string given\\.$#" + count: 1 + path: ../../src/Installer.php + + - + message: "#^Static method Dotenv\\\\Dotenv\\:\\:create\\(\\) invoked with 1 parameter, 2\\-5 required\\.$#" + count: 1 + path: ../../src/Installer.php diff --git a/tests/phpstan/ignore-errors-by-installed-versions.neon.php b/tests/phpstan/ignore-errors-by-installed-versions.neon.php new file mode 100644 index 0000000..62b7f20 --- /dev/null +++ b/tests/phpstan/ignore-errors-by-installed-versions.neon.php @@ -0,0 +1,130 @@ += 80000 ) { + $config['includes'][] = __DIR__ . '/baseline-php-8.neon'; +} else { + $config['includes'][] = __DIR__ . '/baseline-php-7.neon'; +} + +$cwd = getcwd(); +if ( ! is_string( $cwd ) ) { + return $config; +} + +if ( file_exists( $cwd . '/vendor/composer/installed.php' ) ) { + $projectInstalled = require $cwd . '/vendor/composer/installed.php'; +} elseif ( file_exists( $cwd . '/vendor/composer/installed.json' ) ) { + $json = file_get_contents( $cwd . '/vendor/composer/installed.json' ); + if ( ! is_string( $json ) ) { + return $config; + } + + $installed = json_decode( $json, true ); + if ( ! is_array( $installed ) ) { + return $config; + } + + $projectInstalled = array( + 'root' => array(), + 'versions' => array(), + ); + + $packages = isset( $installed['packages'] ) ? $installed['packages'] : $installed; + + if ( is_array( $packages ) ) { + foreach ( $packages as $package ) { + $projectInstalled['versions'][ $package['name'] ] = array( + 'pretty_version' => $package['version'], + 'version' => $package['version_normalized'], + 'reference' => ( + isset( $package['dist']['reference'] ) + ? $package['dist']['reference'] + : ( + isset( $package['source']['reference'] ) + ? $package['source']['reference'] + : null + ) + ), + 'type' => $package['type'], + 'install_path' => ( + isset( $package['install-path'] ) + ? __DIR__ . '/' . $package['install-path'] + : null + ), + 'aliases' => ( + isset( $package['extra']['branch-alias'] ) + ? array_values( $package['extra']['branch-alias'] ) + : array() + ), + 'dev_requirement' => ( + isset( $installed['dev-package-names'] ) + ? in_array( $package['name'], $installed['dev-package-names'], true ) + : false + ), + ); + } + } +} + +if ( empty( $projectInstalled['versions'] ) ) { + return $config; +} + +$pharInstalled = InstalledVersions::getAllRawData(); +InstalledVersions::reload( $projectInstalled ); + +$versionParser = new VersionParser(); + +if ( InstalledVersions::isInstalled( 'composer/composer' ) ) { + if ( InstalledVersions::satisfies( $versionParser, 'composer/composer', '^1') ) { + $config['includes'][] = __DIR__ . '/baseline-composer-1.neon'; + } else { + $config['includes'][] = __DIR__ . '/baseline-composer-2.neon'; + } +} + +if ( InstalledVersions::isInstalled( 'vlucas/phpdotenv' ) ) { + if ( InstalledVersions::satisfies( $versionParser, 'vlucas/phpdotenv', '^3') ) { + $config['includes'][] = __DIR__ . '/baseline-phpdotenv-3.neon'; + } elseif ( InstalledVersions::satisfies( $versionParser, 'vlucas/phpdotenv', '^4') ) { + $config['includes'][] = __DIR__ . '/baseline-phpdotenv-4.neon'; + } else { + $config['includes'][] = __DIR__ . '/baseline-phpdotenv-5.neon'; + } +} + +InstalledVersions::reload( $pharInstalled ? end( $pharInstalled[0] ) : array() ); + +return $config;