class Plugin
Same name in this branch
- 11.1.x vendor/phpstan/extension-installer/src/Plugin.php \PHPStan\ExtensionInstaller\Plugin
- 11.1.x vendor/php-http/discovery/src/Composer/Plugin.php \Http\Discovery\Composer\Plugin
- 11.1.x vendor/composer/installers/src/Composer/Installers/Plugin.php \Composer\Installers\Plugin
- 11.1.x vendor/tbachert/spi/src/Composer/Plugin.php \Nevay\SPI\Composer\Plugin
- 11.1.x composer/Plugin/Scaffold/Plugin.php \Drupal\Composer\Plugin\Scaffold\Plugin
- 11.1.x core/lib/Drupal/Component/Annotation/Plugin.php \Drupal\Component\Annotation\Plugin
- 11.1.x core/lib/Drupal/Component/Plugin/Attribute/Plugin.php \Drupal\Component\Plugin\Attribute\Plugin
PHP_CodeSniffer standard installation manager.
@author Franck Nijhof <franck.nijhof@dealerdirect.com>
Hierarchy
- class \PHPCSStandards\Composer\Plugin\Installers\PHPCodeSniffer\Plugin implements \Composer\Plugin\PluginInterface, \Composer\EventDispatcher\EventSubscriberInterface
Expanded class hierarchy of Plugin
14 string references to 'Plugin'
- Action::create in core/
modules/ system/ src/ Entity/ Action.php - Constructs a new entity object, without permanently saving it.
- block.schema.yml in core/
modules/ block/ config/ schema/ block.schema.yml - core/modules/block/config/schema/block.schema.yml
- DisplayPluginBase::calculateCacheMetadata in core/
modules/ views/ src/ Plugin/ views/ display/ DisplayPluginBase.php - Calculates the display's cache metadata by inspecting each handler/plugin.
- DisplayPluginBase::getAllPlugins in core/
modules/ views/ src/ Plugin/ views/ display/ DisplayPluginBase.php - Gets all the plugins used by the display.
- EntityBlock::getEntityId in core/
modules/ block/ src/ Plugin/ migrate/ destination/ EntityBlock.php - Gets the entity ID of the row.
File
-
vendor/
dealerdirect/ phpcodesniffer-composer-installer/ src/ Plugin.php, line 35
Namespace
PHPCSStandards\Composer\Plugin\Installers\PHPCodeSnifferView source
class Plugin implements PluginInterface, EventSubscriberInterface {
const KEY_MAX_DEPTH = 'phpcodesniffer-search-depth';
const MESSAGE_ERROR_WRONG_MAX_DEPTH = 'The value of "%s" (in the composer.json "extra".section) must be an integer larger than %d, %s given.';
const MESSAGE_NOT_INSTALLED = 'PHPCodeSniffer is not installed';
const MESSAGE_NOTHING_TO_INSTALL = 'No PHPCS standards to install or update';
const MESSAGE_PLUGIN_UNINSTALLED = 'PHPCodeSniffer Composer Installer is uninstalled';
const MESSAGE_RUNNING_INSTALLER = 'Running PHPCodeSniffer Composer Installer';
const PACKAGE_NAME = 'squizlabs/php_codesniffer';
const PACKAGE_TYPE = 'phpcodesniffer-standard';
const PHPCS_CONFIG_REGEX = '`%s:[^\\r\\n]+`';
const PHPCS_CONFIG_KEY = 'installed_paths';
const PLUGIN_NAME = 'dealerdirect/phpcodesniffer-composer-installer';
/**
* @var Composer
*/
private $composer;
/**
* @var string
*/
private $cwd;
/**
* @var Filesystem
*/
private $filesystem;
/**
* @var array
*/
private $installedPaths;
/**
* @var IOInterface
*/
private $io;
/**
* @var ProcessExecutor
*/
private $processExecutor;
/**
* Triggers the plugin's main functionality.
*
* Makes it possible to run the plugin as a custom command.
*
* @param Event $event
*
* @throws \InvalidArgumentException
* @throws \RuntimeException
* @throws LogicException
* @throws ProcessFailedException
* @throws RuntimeException
*/
public static function run(Event $event) {
$io = $event->getIO();
$composer = $event->getComposer();
$instance = new static();
$instance->io = $io;
$instance->composer = $composer;
$instance->init();
$instance->onDependenciesChangedEvent();
}
/**
* {@inheritDoc}
*
* @throws \RuntimeException
* @throws LogicException
* @throws ProcessFailedException
* @throws RuntimeException
*/
public function activate(Composer $composer, IOInterface $io) {
$this->composer = $composer;
$this->io = $io;
$this->init();
}
/**
* {@inheritDoc}
*/
public function deactivate(Composer $composer, IOInterface $io) {
}
/**
* {@inheritDoc}
*/
public function uninstall(Composer $composer, IOInterface $io) {
}
/**
* Prepares the plugin so it's main functionality can be run.
*
* @throws \RuntimeException
* @throws LogicException
* @throws ProcessFailedException
* @throws RuntimeException
*/
private function init() {
$this->cwd = getcwd();
$this->installedPaths = array();
$this->processExecutor = new ProcessExecutor($this->io);
$this->filesystem = new Filesystem($this->processExecutor);
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents() {
return array(
ScriptEvents::POST_INSTALL_CMD => array(
array(
'onDependenciesChangedEvent',
0,
),
),
ScriptEvents::POST_UPDATE_CMD => array(
array(
'onDependenciesChangedEvent',
0,
),
),
);
}
/**
* Entry point for post install and post update events.
*
* @throws \InvalidArgumentException
* @throws LogicException
* @throws ProcessFailedException
* @throws RuntimeException
*/
public function onDependenciesChangedEvent() {
$io = $this->io;
$isVerbose = $io->isVerbose();
$exitCode = 0;
if ($isVerbose) {
$io->write(sprintf('<info>%s</info>', self::MESSAGE_RUNNING_INSTALLER));
}
if ($this->isPHPCodeSnifferInstalled() === true) {
$this->loadInstalledPaths();
$installPathCleaned = $this->cleanInstalledPaths();
$installPathUpdated = $this->updateInstalledPaths();
if ($installPathCleaned === true || $installPathUpdated === true) {
$exitCode = $this->saveInstalledPaths();
}
elseif ($isVerbose) {
$io->write(sprintf('<info>%s</info>', self::MESSAGE_NOTHING_TO_INSTALL));
}
}
else {
$pluginPackage = $this->composer
->getRepositoryManager()
->getLocalRepository()
->findPackages(self::PLUGIN_NAME);
$isPluginUninstalled = count($pluginPackage) === 0;
if ($isPluginUninstalled) {
if ($isVerbose) {
$io->write(sprintf('<info>%s</info>', self::MESSAGE_PLUGIN_UNINSTALLED));
}
}
else {
$exitCode = 1;
if ($isVerbose) {
$io->write(sprintf('<error>%s</error>', self::MESSAGE_NOT_INSTALLED));
}
}
}
return $exitCode;
}
/**
* Load all paths from PHP_CodeSniffer into an array.
*
* @throws LogicException
* @throws ProcessFailedException
* @throws RuntimeException
*/
private function loadInstalledPaths() {
if ($this->isPHPCodeSnifferInstalled() === true) {
$this->processExecutor
->execute($this->getPhpcsCommand() . ' --config-show', $output, $this->getPHPCodeSnifferInstallPath());
$regex = sprintf(self::PHPCS_CONFIG_REGEX, self::PHPCS_CONFIG_KEY);
if (preg_match($regex, $output, $match) === 1) {
$phpcsInstalledPaths = str_replace(self::PHPCS_CONFIG_KEY . ': ', '', $match[0]);
$phpcsInstalledPaths = trim($phpcsInstalledPaths);
if ($phpcsInstalledPaths !== '') {
$this->installedPaths = explode(',', $phpcsInstalledPaths);
}
}
}
}
/**
* Save all coding standard paths back into PHP_CodeSniffer
*
* @throws LogicException
* @throws ProcessFailedException
* @throws RuntimeException
*
* @return int Exit code. 0 for success, 1 or higher for failure.
*/
private function saveInstalledPaths() {
// Check if we found installed paths to set.
if (count($this->installedPaths) !== 0) {
sort($this->installedPaths);
$paths = implode(',', $this->installedPaths);
$arguments = array(
'--config-set',
self::PHPCS_CONFIG_KEY,
$paths,
);
$configMessage = sprintf('PHP CodeSniffer Config <info>%s</info> <comment>set to</comment> <info>%s</info>', self::PHPCS_CONFIG_KEY, $paths);
}
else {
// Delete the installed paths if none were found.
$arguments = array(
'--config-delete',
self::PHPCS_CONFIG_KEY,
);
$configMessage = sprintf('PHP CodeSniffer Config <info>%s</info> <comment>delete</comment>', self::PHPCS_CONFIG_KEY);
}
// Prepare message in case of failure
$failMessage = sprintf('Failed to set PHP CodeSniffer <info>%s</info> Config', self::PHPCS_CONFIG_KEY);
// Okay, lets rock!
$command = vsprintf('%s %s', array(
'phpcs command' => $this->getPhpcsCommand(),
'arguments' => implode(' ', $arguments),
));
$exitCode = $this->processExecutor
->execute($command, $configResult, $this->getPHPCodeSnifferInstallPath());
if ($exitCode === 0) {
$exitCode = $this->verifySaveSuccess();
}
if ($exitCode === 0) {
$this->io
->write($configMessage);
}
else {
$this->io
->write($failMessage);
}
if ($this->io
->isVerbose() && !empty($configResult)) {
$this->io
->write(sprintf('<info>%s</info>', $configResult));
}
return $exitCode;
}
/**
* Verify that the paths which were expected to be saved, have been.
*
* @return int Exit code. 0 for success, 1 for failure.
*/
private function verifySaveSuccess() {
$exitCode = 1;
$expectedPaths = $this->installedPaths;
// Request the currently set installed paths after the save.
$this->loadInstalledPaths();
$registeredPaths = array_intersect($this->installedPaths, $expectedPaths);
$registeredCount = count($registeredPaths);
$expectedCount = count($expectedPaths);
if ($expectedCount === $registeredCount) {
$exitCode = 0;
}
if ($exitCode === 1 && $this->io
->isVerbose()) {
$verificationMessage = sprintf("Paths to external standards found by the plugin: <info>%s</info>\n" . 'Actual paths registered with PHPCS: <info>%s</info>', implode(', ', $expectedPaths), implode(', ', $this->installedPaths));
$this->io
->write($verificationMessage);
}
return $exitCode;
}
/**
* Get the command to call PHPCS.
*/
protected function getPhpcsCommand() {
// Determine the path to the main PHPCS file.
$phpcsPath = $this->getPHPCodeSnifferInstallPath();
if (file_exists($phpcsPath . '/bin/phpcs') === true) {
// PHPCS 3.x.
$phpcsExecutable = './bin/phpcs';
}
else {
// PHPCS 2.x.
$phpcsExecutable = './scripts/phpcs';
}
return vsprintf('%s %s', array(
'php executable' => $this->getPhpExecCommand(),
'phpcs executable' => $phpcsExecutable,
));
}
/**
* Get the path to the current PHP version being used.
*
* Duplicate of the same in the EventDispatcher class in Composer itself.
*/
protected function getPhpExecCommand() {
$finder = new PhpExecutableFinder();
$phpPath = $finder->find(false);
if ($phpPath === false) {
throw new \RuntimeException('Failed to locate PHP binary to execute ' . $phpPath);
}
$phpArgs = $finder->findArguments();
$phpArgs = $phpArgs ? ' ' . implode(' ', $phpArgs) : '';
$command = ProcessExecutor::escape($phpPath) . $phpArgs . ' -d allow_url_fopen=' . ProcessExecutor::escape(ini_get('allow_url_fopen')) . ' -d disable_functions=' . ProcessExecutor::escape(ini_get('disable_functions')) . ' -d memory_limit=' . ProcessExecutor::escape(ini_get('memory_limit'));
return $command;
}
/**
* Iterate trough all known paths and check if they are still valid.
*
* If path does not exists, is not an directory or isn't readable, the path
* is removed from the list.
*
* @return bool True if changes where made, false otherwise
*/
private function cleanInstalledPaths() {
$changes = false;
foreach ($this->installedPaths as $key => $path) {
// This might be a relative path as well
$alternativePath = realpath($this->getPHPCodeSnifferInstallPath() . \DIRECTORY_SEPARATOR . $path);
if ((is_dir($path) === false || is_readable($path) === false) && ($alternativePath === false || is_dir($alternativePath) === false || is_readable($alternativePath) === false)) {
unset($this->installedPaths[$key]);
$changes = true;
}
}
return $changes;
}
/**
* Check all installed packages (including the root package) against
* the installed paths from PHP_CodeSniffer and add the missing ones.
*
* @return bool True if changes where made, false otherwise
*
* @throws \InvalidArgumentException
* @throws \RuntimeException
*/
private function updateInstalledPaths() {
$changes = false;
$searchPaths = array();
// Add root package only if it has the expected package type.
if ($this->composer
->getPackage() instanceof RootPackageInterface && $this->composer
->getPackage()
->getType() === self::PACKAGE_TYPE) {
$searchPaths[] = $this->cwd;
}
$codingStandardPackages = $this->getPHPCodingStandardPackages();
foreach ($codingStandardPackages as $package) {
$installPath = $this->composer
->getInstallationManager()
->getInstallPath($package);
if ($this->filesystem
->isAbsolutePath($installPath) === false) {
$installPath = $this->filesystem
->normalizePath($this->cwd . \DIRECTORY_SEPARATOR . $installPath);
}
$searchPaths[] = $installPath;
}
// Nothing to do.
if ($searchPaths === array()) {
return false;
}
$finder = new Finder();
$finder->files()
->depth('<= ' . $this->getMaxDepth())
->depth('>= ' . $this->getMinDepth())
->ignoreUnreadableDirs()
->ignoreVCS(true)
->in($searchPaths)
->name('ruleset.xml');
// Process each found possible ruleset.
foreach ($finder as $ruleset) {
$standardsPath = $ruleset->getPath();
// Pick the directory above the directory containing the standard, unless this is the project root.
if ($standardsPath !== $this->cwd) {
$standardsPath = dirname($standardsPath);
}
// Use relative paths for local project repositories.
if ($this->isRunningGlobally() === false) {
$standardsPath = $this->filesystem
->findShortestPath($this->getPHPCodeSnifferInstallPath(), $standardsPath, true);
}
// De-duplicate and add when directory is not configured.
if (in_array($standardsPath, $this->installedPaths, true) === false) {
$this->installedPaths[] = $standardsPath;
$changes = true;
}
}
return $changes;
}
/**
* Iterates through Composers' local repository looking for valid Coding
* Standard packages.
*
* @return array Composer packages containing coding standard(s)
*/
private function getPHPCodingStandardPackages() {
$codingStandardPackages = array_filter($this->composer
->getRepositoryManager()
->getLocalRepository()
->getPackages(), function (PackageInterface $package) {
if ($package instanceof AliasPackage) {
return false;
}
return $package->getType() === Plugin::PACKAGE_TYPE;
});
return $codingStandardPackages;
}
/**
* Searches for the installed PHP_CodeSniffer Composer package
*
* @param null|string|\Composer\Semver\Constraint\ConstraintInterface $versionConstraint to match against
*
* @return PackageInterface|null
*/
private function getPHPCodeSnifferPackage($versionConstraint = null) {
$packages = $this->composer
->getRepositoryManager()
->getLocalRepository()
->findPackages(self::PACKAGE_NAME, $versionConstraint);
return array_shift($packages);
}
/**
* Returns the path to the PHP_CodeSniffer package installation location
*
* @return string
*/
private function getPHPCodeSnifferInstallPath() {
return $this->composer
->getInstallationManager()
->getInstallPath($this->getPHPCodeSnifferPackage());
}
/**
* Simple check if PHP_CodeSniffer is installed.
*
* @param null|string|\Composer\Semver\Constraint\ConstraintInterface $versionConstraint to match against
*
* @return bool Whether PHP_CodeSniffer is installed
*/
private function isPHPCodeSnifferInstalled($versionConstraint = null) {
return $this->getPHPCodeSnifferPackage($versionConstraint) !== null;
}
/**
* Test if composer is running "global"
* This check kinda dirty, but it is the "Composer Way"
*
* @return bool Whether Composer is running "globally"
*
* @throws \RuntimeException
*/
private function isRunningGlobally() {
return $this->composer
->getConfig()
->get('home') === $this->cwd;
}
/**
* Determines the maximum search depth when searching for Coding Standards.
*
* @return int
*
* @throws \InvalidArgumentException
*/
private function getMaxDepth() {
$maxDepth = 3;
$extra = $this->composer
->getPackage()
->getExtra();
if (array_key_exists(self::KEY_MAX_DEPTH, $extra)) {
$maxDepth = $extra[self::KEY_MAX_DEPTH];
$minDepth = $this->getMinDepth();
if ((string) (int) $maxDepth !== (string) $maxDepth || $maxDepth <= $minDepth || is_float($maxDepth) === true) {
$message = vsprintf(self::MESSAGE_ERROR_WRONG_MAX_DEPTH, array(
'key' => self::KEY_MAX_DEPTH,
'min' => $minDepth,
'given' => var_export($maxDepth, true),
));
throw new \InvalidArgumentException($message);
}
}
return (int) $maxDepth;
}
/**
* Returns the minimal search depth for Coding Standard packages.
*
* Usually this is 0, unless PHP_CodeSniffer >= 3 is used.
*
* @return int
*/
private function getMinDepth() {
if ($this->isPHPCodeSnifferInstalled('>= 3.0.0') !== true) {
return 1;
}
return 0;
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title |
---|---|---|---|---|
Plugin::$composer | private | property | ||
Plugin::$cwd | private | property | ||
Plugin::$filesystem | private | property | ||
Plugin::$installedPaths | private | property | ||
Plugin::$io | private | property | ||
Plugin::$processExecutor | private | property | ||
Plugin::activate | public | function | Overrides PluginInterface::activate | |
Plugin::cleanInstalledPaths | private | function | Iterate trough all known paths and check if they are still valid. | |
Plugin::deactivate | public | function | Remove any hooks from Composer | Overrides PluginInterface::deactivate |
Plugin::getMaxDepth | private | function | Determines the maximum search depth when searching for Coding Standards. | |
Plugin::getMinDepth | private | function | Returns the minimal search depth for Coding Standard packages. | |
Plugin::getPHPCodeSnifferInstallPath | private | function | Returns the path to the PHP_CodeSniffer package installation location | |
Plugin::getPHPCodeSnifferPackage | private | function | Searches for the installed PHP_CodeSniffer Composer package | |
Plugin::getPHPCodingStandardPackages | private | function | Iterates through Composers' local repository looking for valid Coding Standard packages. |
|
Plugin::getPhpcsCommand | protected | function | Get the command to call PHPCS. | |
Plugin::getPhpExecCommand | protected | function | Get the path to the current PHP version being used. | |
Plugin::getSubscribedEvents | public static | function | Returns an array of event names this subscriber wants to listen to. | Overrides EventSubscriberInterface::getSubscribedEvents |
Plugin::init | private | function | Prepares the plugin so it's main functionality can be run. | |
Plugin::isPHPCodeSnifferInstalled | private | function | Simple check if PHP_CodeSniffer is installed. | |
Plugin::isRunningGlobally | private | function | Test if composer is running "global" This check kinda dirty, but it is the "Composer Way" |
|
Plugin::KEY_MAX_DEPTH | constant | |||
Plugin::loadInstalledPaths | private | function | Load all paths from PHP_CodeSniffer into an array. | |
Plugin::MESSAGE_ERROR_WRONG_MAX_DEPTH | constant | |||
Plugin::MESSAGE_NOTHING_TO_INSTALL | constant | |||
Plugin::MESSAGE_NOT_INSTALLED | constant | |||
Plugin::MESSAGE_PLUGIN_UNINSTALLED | constant | |||
Plugin::MESSAGE_RUNNING_INSTALLER | constant | |||
Plugin::onDependenciesChangedEvent | public | function | Entry point for post install and post update events. | |
Plugin::PACKAGE_NAME | constant | |||
Plugin::PACKAGE_TYPE | constant | |||
Plugin::PHPCS_CONFIG_KEY | constant | |||
Plugin::PHPCS_CONFIG_REGEX | constant | |||
Plugin::PLUGIN_NAME | constant | |||
Plugin::run | public static | function | Triggers the plugin's main functionality. | |
Plugin::saveInstalledPaths | private | function | Save all coding standard paths back into PHP_CodeSniffer | |
Plugin::uninstall | public | function | Prepare the plugin to be uninstalled | Overrides PluginInterface::uninstall |
Plugin::updateInstalledPaths | private | function | Check all installed packages (including the root package) against the installed paths from PHP_CodeSniffer and add the missing ones. |
|
Plugin::verifySaveSuccess | private | function | Verify that the paths which were expected to be saved, have been. | |
PluginInterface::PLUGIN_API_VERSION | public | constant | Version number of the internal composer-plugin-api package |