Complex classes like Injector often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Injector, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 128 | class Injector implements ContainerInterface |
||
| 129 | { |
||
| 130 | |||
| 131 | /** |
||
| 132 | * Local store of all services |
||
| 133 | * |
||
| 134 | * @var array |
||
| 135 | */ |
||
| 136 | private $serviceCache; |
||
| 137 | |||
| 138 | /** |
||
| 139 | * Cache of items that need to be mapped for each service that gets injected |
||
| 140 | * |
||
| 141 | * @var array |
||
| 142 | */ |
||
| 143 | private $injectMap; |
||
| 144 | |||
| 145 | /** |
||
| 146 | * A store of all the service configurations that have been defined. |
||
| 147 | * |
||
| 148 | * @var array |
||
| 149 | */ |
||
| 150 | private $specs; |
||
| 151 | |||
| 152 | /** |
||
| 153 | * A map of all the properties that should be automagically set on all |
||
| 154 | * objects instantiated by the injector |
||
| 155 | */ |
||
| 156 | private $autoProperties; |
||
| 157 | |||
| 158 | /** |
||
| 159 | * A singleton if you want to use it that way |
||
| 160 | * |
||
| 161 | * @var Injector |
||
| 162 | */ |
||
| 163 | private static $instance; |
||
| 164 | |||
| 165 | /** |
||
| 166 | * Indicates whether or not to automatically scan properties in injected objects to auto inject |
||
| 167 | * stuff, similar to the way grails does things. |
||
| 168 | * |
||
| 169 | * @var boolean |
||
| 170 | */ |
||
| 171 | private $autoScanProperties = false; |
||
| 172 | |||
| 173 | /** |
||
| 174 | * The default factory used to create new instances. |
||
| 175 | * |
||
| 176 | * The {@link InjectionCreator} is used by default, which simply directly |
||
| 177 | * creates objects. This can be changed to use a different default creation |
||
| 178 | * method if desired. |
||
| 179 | * |
||
| 180 | * Each individual component can also specify a custom factory to use by |
||
| 181 | * using the `factory` parameter. |
||
| 182 | * |
||
| 183 | * @var Factory |
||
| 184 | */ |
||
| 185 | protected $objectCreator; |
||
| 186 | |||
| 187 | /** |
||
| 188 | * Locator for determining Config properties for services |
||
| 189 | * |
||
| 190 | * @var ServiceConfigurationLocator |
||
| 191 | */ |
||
| 192 | protected $configLocator; |
||
| 193 | |||
| 194 | /** |
||
| 195 | * Create a new injector. |
||
| 196 | * |
||
| 197 | * @param array $config |
||
| 198 | * Service configuration |
||
| 199 | */ |
||
| 200 | public function __construct($config = null) |
||
| 227 | |||
| 228 | /** |
||
| 229 | * The injector instance this one was copied from when Injector::nest() was called. |
||
| 230 | * |
||
| 231 | * @var Injector |
||
| 232 | */ |
||
| 233 | protected $nestedFrom = null; |
||
| 234 | |||
| 235 | /** |
||
| 236 | * If a user wants to use the injector as a static reference |
||
| 237 | * |
||
| 238 | * @param array $config |
||
| 239 | * @return ContainerInterface |
||
| 240 | */ |
||
| 241 | public static function inst($config = null) |
||
| 248 | |||
| 249 | /** |
||
| 250 | * Sets the default global injector instance. |
||
| 251 | * |
||
| 252 | * @param ContainerInterface $instance |
||
| 253 | * @return Injector Reference to new active Injector instance |
||
| 254 | */ |
||
| 255 | public static function set_inst(ContainerInterface $instance) |
||
| 259 | |||
| 260 | /** |
||
| 261 | * Make the newly active {@link Injector} be a copy of the current active |
||
| 262 | * {@link Injector} instance. |
||
| 263 | * |
||
| 264 | * You can then make changes to the injector with methods such as |
||
| 265 | * {@link Injector::inst()->registerService()} which will be discarded |
||
| 266 | * upon a subsequent call to {@link Injector::unnest()} |
||
| 267 | * |
||
| 268 | * @return Injector Reference to new active Injector instance |
||
| 269 | */ |
||
| 270 | public static function nest() |
||
| 278 | |||
| 279 | /** |
||
| 280 | * Change the active Injector back to the Injector instance the current active |
||
| 281 | * Injector object was copied from. |
||
| 282 | * |
||
| 283 | * @return Injector Reference to restored active Injector instance |
||
| 284 | */ |
||
| 285 | public static function unnest() |
||
| 297 | |||
| 298 | /** |
||
| 299 | * Indicate whether we auto scan injected objects for properties to set. |
||
| 300 | * |
||
| 301 | * @param boolean $val |
||
| 302 | */ |
||
| 303 | public function setAutoScanProperties($val) |
||
| 307 | |||
| 308 | /** |
||
| 309 | * Sets the default factory to use for creating new objects. |
||
| 310 | * |
||
| 311 | * @param \SilverStripe\Core\Injector\Factory $obj |
||
| 312 | */ |
||
| 313 | public function setObjectCreator(Factory $obj) |
||
| 317 | |||
| 318 | /** |
||
| 319 | * @return Factory |
||
| 320 | */ |
||
| 321 | public function getObjectCreator() |
||
| 325 | |||
| 326 | /** |
||
| 327 | * Set the configuration locator |
||
| 328 | * @param ServiceConfigurationLocator $configLocator |
||
| 329 | */ |
||
| 330 | public function setConfigLocator($configLocator) |
||
| 334 | |||
| 335 | /** |
||
| 336 | * Retrieve the configuration locator |
||
| 337 | * @return ServiceConfigurationLocator |
||
| 338 | */ |
||
| 339 | public function getConfigLocator() |
||
| 343 | |||
| 344 | /** |
||
| 345 | * Add in a specific mapping that should be catered for on a type. |
||
| 346 | * This allows configuration of what should occur when an object |
||
| 347 | * of a particular type is injected, and what items should be injected |
||
| 348 | * for those properties / methods. |
||
| 349 | * |
||
| 350 | * @param string $class The class to set a mapping for |
||
| 351 | * @param string $property The property to set the mapping for |
||
| 352 | * @param string $toInject The registered type that will be injected |
||
| 353 | * @param string $injectVia Whether to inject by setting a property or calling a setter |
||
| 354 | */ |
||
| 355 | public function setInjectMapping($class, $property, $toInject, $injectVia = 'property') |
||
| 363 | |||
| 364 | /** |
||
| 365 | * Add an object that should be automatically set on managed objects |
||
| 366 | * |
||
| 367 | * This allows you to specify, for example, that EVERY managed object |
||
| 368 | * will be automatically inject with a log object by the following |
||
| 369 | * |
||
| 370 | * $injector->addAutoProperty('log', new Logger()); |
||
| 371 | * |
||
| 372 | * @param string $property |
||
| 373 | * the name of the property |
||
| 374 | * @param object $object |
||
| 375 | * the object to be set |
||
| 376 | * @return $this |
||
| 377 | */ |
||
| 378 | public function addAutoProperty($property, $object) |
||
| 383 | |||
| 384 | /** |
||
| 385 | * Load services using the passed in configuration for those services |
||
| 386 | * |
||
| 387 | * @param array $config |
||
| 388 | * @return $this |
||
| 389 | */ |
||
| 390 | public function load($config = array()) |
||
| 456 | |||
| 457 | /** |
||
| 458 | * Update the configuration of an already defined service |
||
| 459 | * |
||
| 460 | * Use this if you don't want to register a complete new config, just append |
||
| 461 | * to an existing configuration. Helpful to avoid overwriting someone else's changes |
||
| 462 | * |
||
| 463 | * updateSpec('RequestProcessor', 'filters', '%$MyFilter') |
||
| 464 | * |
||
| 465 | * @param string $id |
||
| 466 | * The name of the service to update the definition for |
||
| 467 | * @param string $property |
||
| 468 | * The name of the property to update. |
||
| 469 | * @param mixed $value |
||
| 470 | * The value to set |
||
| 471 | * @param boolean $append |
||
| 472 | * Whether to append (the default) when the property is an array |
||
| 473 | */ |
||
| 474 | public function updateSpec($id, $property, $value, $append = true) |
||
| 492 | |||
| 493 | /** |
||
| 494 | * Update a class specification to convert constructor configuration information if needed |
||
| 495 | * |
||
| 496 | * We do this as a separate process to avoid unneeded calls to convertServiceProperty |
||
| 497 | * |
||
| 498 | * @param array $spec |
||
| 499 | * The class specification to update |
||
| 500 | */ |
||
| 501 | protected function updateSpecConstructor(&$spec) |
||
| 507 | |||
| 508 | /** |
||
| 509 | * Recursively convert a value into its proper representation with service references |
||
| 510 | * resolved to actual objects |
||
| 511 | * |
||
| 512 | * @param string $value |
||
| 513 | * @return array|mixed|string |
||
| 514 | */ |
||
| 515 | public function convertServiceProperty($value) |
||
| 544 | |||
| 545 | /** |
||
| 546 | * Instantiate a managed object |
||
| 547 | * |
||
| 548 | * Given a specification of the form |
||
| 549 | * |
||
| 550 | * array( |
||
| 551 | * 'class' => 'ClassName', |
||
| 552 | * 'properties' => array('property' => 'scalar', 'other' => '%$BeanRef') |
||
| 553 | * 'id' => 'ServiceId', |
||
| 554 | * 'type' => 'singleton|prototype' |
||
| 555 | * ) |
||
| 556 | * |
||
| 557 | * will create a new object, store it in the service registry, and |
||
| 558 | * set any relevant properties |
||
| 559 | * |
||
| 560 | * Optionally, you can pass a class name directly for creation |
||
| 561 | * |
||
| 562 | * To access this from the outside, you should call ->get('Name') to ensure |
||
| 563 | * the appropriate checks are made on the specific type. |
||
| 564 | * |
||
| 565 | * |
||
| 566 | * @param array $spec |
||
| 567 | * The specification of the class to instantiate |
||
| 568 | * @param string $id |
||
| 569 | * The name of the object being created. If not supplied, then the id will be inferred from the |
||
| 570 | * object being created |
||
| 571 | * @param string $type |
||
| 572 | * Whether to create as a singleton or prototype object. Allows code to be explicit as to how it |
||
| 573 | * wants the object to be returned |
||
| 574 | * @return object |
||
| 575 | */ |
||
| 576 | protected function instantiate($spec, $id = null, $type = null) |
||
| 615 | |||
| 616 | /** |
||
| 617 | * Inject $object with available objects from the service cache |
||
| 618 | * |
||
| 619 | * @todo Track all the existing objects that have had a service bound |
||
| 620 | * into them, so we can update that binding at a later point if needbe (ie |
||
| 621 | * if the managed service changes) |
||
| 622 | * |
||
| 623 | * @param object $object |
||
| 624 | * The object to inject |
||
| 625 | * @param string $asType |
||
| 626 | * The ID this item was loaded as. This is so that the property configuration |
||
| 627 | * for a type is referenced correctly in case $object is no longer the same |
||
| 628 | * type as the loaded config specification had it as. |
||
| 629 | */ |
||
| 630 | public function inject($object, $asType = null) |
||
| 768 | |||
| 769 | /** |
||
| 770 | * Helper to set a property's value |
||
| 771 | * |
||
| 772 | * @param object $object |
||
| 773 | * Set an object's property to a specific value |
||
| 774 | * @param string $name |
||
| 775 | * The name of the property to set |
||
| 776 | * @param mixed $value |
||
| 777 | * The value to set |
||
| 778 | */ |
||
| 779 | protected function setObjectProperty($object, $name, $value) |
||
| 787 | |||
| 788 | /** |
||
| 789 | * @deprecated 4.0.0:5.0.0 Use Injector::has() instead |
||
| 790 | * @param $name |
||
| 791 | * @return string |
||
| 792 | */ |
||
| 793 | public function hasService($name) |
||
| 799 | |||
| 800 | /** |
||
| 801 | * Does the given service exist? |
||
| 802 | * |
||
| 803 | * We do a special check here for services that are using compound names. For example, |
||
| 804 | * we might want to say that a property should be injected with Log.File or Log.Memory, |
||
| 805 | * but have only registered a 'Log' service, we'll instead return that. |
||
| 806 | * |
||
| 807 | * Will recursively call itself for each depth of dotting. |
||
| 808 | * |
||
| 809 | * @param string $name |
||
| 810 | * @return boolean |
||
| 811 | */ |
||
| 812 | public function has($name) |
||
| 816 | |||
| 817 | /** |
||
| 818 | * Does the given service exist, and if so, what's the stored name for it? |
||
| 819 | * |
||
| 820 | * We do a special check here for services that are using compound names. For example, |
||
| 821 | * we might want to say that a property should be injected with Log.File or Log.Memory, |
||
| 822 | * but have only registered a 'Log' service, we'll instead return that. |
||
| 823 | * |
||
| 824 | * Will recursively call itself for each depth of dotting. |
||
| 825 | * |
||
| 826 | * @param string $name |
||
| 827 | * @return string|null The name of the service (as it might be different from the one passed in) |
||
| 828 | */ |
||
| 829 | public function getServiceName($name) |
||
| 844 | |||
| 845 | /** |
||
| 846 | * Register a service object with an optional name to register it as the |
||
| 847 | * service for |
||
| 848 | * |
||
| 849 | * @param object $service The object to register |
||
| 850 | * @param string $replace The name of the object to replace (if different to the |
||
| 851 | * class name of the object to register) |
||
| 852 | */ |
||
| 853 | public function registerService($service, $replace = null) |
||
| 864 | |||
| 865 | /** |
||
| 866 | * Removes a named object from the cached list of objects managed |
||
| 867 | * by the inject |
||
| 868 | * |
||
| 869 | * @param string $name The name to unregister |
||
| 870 | */ |
||
| 871 | public function unregisterNamedObject($name) |
||
| 875 | |||
| 876 | /** |
||
| 877 | * Clear out all objects that are managed by the injetor. |
||
| 878 | */ |
||
| 879 | public function unregisterAllObjects() |
||
| 883 | |||
| 884 | /** |
||
| 885 | * Get a named managed object |
||
| 886 | * |
||
| 887 | * Will first check to see if the item has been registered as a configured service/bean |
||
| 888 | * and return that if so. |
||
| 889 | * |
||
| 890 | * Next, will check to see if there's any registered configuration for the given type |
||
| 891 | * and will then try and load that |
||
| 892 | * |
||
| 893 | * Failing all of that, will just return a new instance of the specified object. |
||
| 894 | * |
||
| 895 | * @throws NotFoundExceptionInterface No entry was found for **this** identifier. |
||
| 896 | * |
||
| 897 | * @param string $name The name of the service to retrieve. If not a registered |
||
| 898 | * service, then a class of the given name is instantiated |
||
| 899 | * @param boolean $asSingleton Whether to register the created object as a singleton |
||
| 900 | * if no other configuration is found |
||
| 901 | * @param array $constructorArgs Optional set of arguments to pass as constructor arguments |
||
| 902 | * if this object is to be created from scratch (with $asSingleton = false) |
||
| 903 | * @return mixed Instance of the specified object |
||
| 904 | */ |
||
| 905 | public function get($name, $asSingleton = true, $constructorArgs = null) |
||
| 915 | |||
| 916 | /** |
||
| 917 | * Returns the service, or `null` if it doesnt' exist. See {@link get()} for main usage. |
||
| 918 | * |
||
| 919 | * @param string $name |
||
| 920 | * @param boolean $asSingleton |
||
| 921 | * @param array $constructorArgs |
||
| 922 | * @return mixed|null Instance of the specified object (if it exists) |
||
| 923 | */ |
||
| 924 | protected function getNamedService($name, $asSingleton = true, $constructorArgs = null) |
||
| 976 | |||
| 977 | /** |
||
| 978 | * Magic method to return an item directly |
||
| 979 | * |
||
| 980 | * @param string $name |
||
| 981 | * The named object to retrieve |
||
| 982 | * @return mixed |
||
| 983 | */ |
||
| 984 | public function __get($name) |
||
| 988 | |||
| 989 | /** |
||
| 990 | * Similar to get() but always returns a new object of the given type |
||
| 991 | * |
||
| 992 | * Additional parameters are passed through as |
||
| 993 | * |
||
| 994 | * @param string $name |
||
| 995 | * @param mixed $argument,... arguments to pass to the constructor |
||
| 996 | * @return mixed A new instance of the specified object |
||
| 997 | */ |
||
| 998 | public function create($name, $argument = null) |
||
| 1004 | |||
| 1005 | /** |
||
| 1006 | * Creates an object with the supplied argument array |
||
| 1007 | * |
||
| 1008 | * @param string $name Name of the class to create an object of |
||
| 1009 | * @param array $constructorArgs Arguments to pass to the constructor |
||
| 1010 | * @return mixed |
||
| 1011 | */ |
||
| 1012 | public function createWithArgs($name, $constructorArgs) |
||
| 1016 | } |
||
| 1017 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)or! empty(...)instead.