123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- <?php
- /**
- * Yoast SEO Plugin File.
- *
- * @package Yoast\YoastSEO\Dependency_Injection
- */
- namespace Yoast\WP\Free\Dependency_Injection;
- use Symfony\Component\Config\FileLocator;
- use Symfony\Component\DependencyInjection\ContainerBuilder;
- use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
- use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
- use Symfony\Component\Config\Resource\GlobResource;
- use Symfony\Component\DependencyInjection\ChildDefinition;
- use Symfony\Component\DependencyInjection\Definition;
- /**
- * This class is mostly a direct copy-paste of the symfony PhpFileLoader class.
- * It's been adapted to allow automatic discovery based on not just PSR-4 but also the Yoast standards.
- */
- class Custom_Loader extends PhpFileLoader {
- /**
- * Custom_Loader constructor.
- *
- * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container The ContainerBuilder to load classes for.
- */
- public function __construct( ContainerBuilder $container ) {
- parent::__construct( $container, new FileLocator( __DIR__ . '/../..' ) );
- }
- /**
- * Transforms a path to a class name using the class map.
- *
- * @param string $path The path of the class.
- *
- * @return bool|string The classname.
- */
- private function getClassFromClassMap( $path ) {
- static $class_map;
- if ( ! $class_map ) {
- $class_map = require __DIR__ . '/../../vendor/composer/autoload_classmap.php';
- }
- foreach ( $class_map as $class => $class_path ) {
- if ( $path === $class_path ) {
- return $class;
- }
- }
- return false;
- }
- /**
- * Registers a set of classes as services using PSR-4 for discovery.
- *
- * @param \Symfony\Component\DependencyInjection\Definition $prototype A definition to use as template.
- * @param string $namespace The namespace prefix of classes
- * in the scanned directory.
- * @param string $resource The directory to look for classes,
- * glob-patterns allowed.
- * @param string $exclude A globed path of files to exclude.
- *
- * @throws InvalidArgumentException If invalid arguments are supplied.
- *
- * @return void
- */
- public function registerClasses( Definition $prototype, $namespace, $resource, $exclude = null ) {
- if ( '\\' !== \substr( $namespace, -1 ) ) {
- throw new InvalidArgumentException( \sprintf( 'Namespace prefix must end with a "\\": %s.', $namespace ) );
- }
- if ( ! \preg_match( '/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+\\\\)++$/', $namespace ) ) {
- throw new InvalidArgumentException( \sprintf( 'Namespace is not a valid PSR-4 prefix: %s.', $namespace ) );
- }
- $classes = $this->findClasses( $namespace, $resource, $exclude );
- // Prepare for deep cloning.
- $serialized_prototype = \serialize( $prototype );
- $interfaces = [];
- $singly_implemented = [];
- foreach ( $classes as $class => $error_message ) {
- if ( \interface_exists( $class, false ) ) {
- $interfaces[] = $class;
- }
- else {
- $this->setDefinition( $class, $definition = \unserialize( $serialized_prototype ) );
- if ( null !== $error_message ) {
- $definition->addError( $error_message );
- continue;
- }
- foreach ( \class_implements( $class, false ) as $interface ) {
- $singly_implemented[ $interface ] = isset( $singly_implemented[ $interface ] ) ? false : $class;
- }
- }
- }
- foreach ( $interfaces as $interface ) {
- if ( ! empty( $singly_implemented[ $interface ] ) ) {
- $this->container->setAlias( $interface, $singly_implemented[ $interface ] )
- ->setPublic( false );
- }
- }
- }
- /**
- * Registers a definition in the container with its instanceof-conditionals.
- *
- * @param string $id The ID of the definition.
- * @param \Symfony\Component\DependencyInjection\Definition $definition The definition.
- *
- * @throws InvalidArgumentException If invalid arguments were supplied.
- *
- * @return void
- */
- protected function setDefinition( $id, Definition $definition ) {
- $this->container->removeBindings( $id );
- // @codingStandardsIgnoreLine WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar This is from an inherited class not abiding by that standard.
- if ( $this->isLoadingInstanceof ) {
- if ( ! $definition instanceof ChildDefinition ) {
- throw new InvalidArgumentException( \sprintf( 'Invalid type definition "%s": ChildDefinition expected, "%s" given.', $id, \get_class( $definition ) ) );
- }
- $this->instanceof[ $id ] = $definition;
- }
- else {
- $this->container->setDefinition( $id, ( $definition instanceof ChildDefinition ) ? $definition : $definition->setInstanceofConditionals( $this->instanceof ) );
- }
- }
- /**
- * Finds classes based on a given pattern and exclude pattern.
- *
- * @param string $namespace The namespace prefix of classes in the scanned directory.
- * @param string $pattern The directory to look for classes, glob-patterns allowed.
- * @param string $exclude A globed path of files to exclude.
- *
- * @throws InvalidArgumentException If invalid arguments were supplied.
- *
- * @return array The found classes.
- */
- private function findClasses( $namespace, $pattern, $exclude ) {
- $parameter_bag = $this->container->getParameterBag();
- $exclude_paths = [];
- $exclude_prefix = null;
- if ( $exclude ) {
- $exclude = $parameter_bag->unescapeValue( $parameter_bag->resolveValue( $exclude ) );
- foreach ( $this->glob( $exclude, true, $resource ) as $path => $info ) {
- if ( null === $exclude_prefix ) {
- $exclude_prefix = $resource->getPrefix();
- }
- // Normalize Windows slashes.
- $exclude_paths[ \str_replace( '\\', '/', $path ) ] = true;
- }
- }
- $pattern = $parameter_bag->unescapeValue( $parameter_bag->resolveValue( $pattern ) );
- $classes = [];
- $ext_regexp = \defined( 'HHVM_VERSION' ) ? '/\\.(?:php|hh)$/' : '/\\.php$/';
- $prefix_len = null;
- foreach ( $this->glob( $pattern, true, $resource ) as $path => $info ) {
- if ( null === $prefix_len ) {
- $prefix_len = \strlen( $resource->getPrefix() );
- if ( $exclude_prefix && 0 !== \strpos( $exclude_prefix, $resource->getPrefix() ) ) {
- throw new InvalidArgumentException( \sprintf( 'Invalid "exclude" pattern when importing classes for "%s": make sure your "exclude" pattern (%s) is a subset of the "resource" pattern (%s)', $namespace, $exclude, $pattern ) );
- }
- }
- if ( isset( $exclude_paths[ \str_replace( '\\', '/', $path ) ] ) ) {
- continue;
- }
- if ( ! \preg_match( $ext_regexp, $path, $m ) || ! $info->isReadable() ) {
- continue;
- }
- $class = $this->getClassFromClassMap( $path );
- if ( ! $class || ! \preg_match( '/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $class ) ) {
- continue;
- }
- try {
- $r = $this->container->getReflectionClass( $class );
- } catch ( \ReflectionException $e ) {
- $classes[ $class ] = \sprintf(
- 'While discovering services from namespace "%s", an error was thrown when processing the class "%s": "%s".',
- $namespace,
- $class,
- $e->getMessage()
- );
- continue;
- }
- // Check to make sure the expected class exists.
- if ( ! $r ) {
- throw new InvalidArgumentException( \sprintf( 'Expected to find class "%s" in file "%s" while importing services from resource "%s", but it was not found! Check the namespace prefix used with the resource.', $class, $path, $pattern ) );
- }
- if ( $r->isInstantiable() || $r->isInterface() ) {
- $classes[ $class ] = null;
- }
- }
- // Track only for new & removed files.
- if ( $resource instanceof GlobResource ) {
- $this->container->addResource( $resource );
- }
- else {
- foreach ( $resource as $path ) {
- $this->container->fileExists( $path, false );
- }
- }
- return $classes;
- }
- }
|