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 |
||
| 125 | class Injector |
||
| 126 | { |
||
| 127 | |||
| 128 | /** |
||
| 129 | * Local store of all services |
||
| 130 | * |
||
| 131 | * @var array |
||
| 132 | */ |
||
| 133 | private $serviceCache; |
||
| 134 | |||
| 135 | /** |
||
| 136 | * Cache of items that need to be mapped for each service that gets injected |
||
| 137 | * |
||
| 138 | * @var array |
||
| 139 | */ |
||
| 140 | private $injectMap; |
||
| 141 | |||
| 142 | /** |
||
| 143 | * A store of all the service configurations that have been defined. |
||
| 144 | * |
||
| 145 | * @var array |
||
| 146 | */ |
||
| 147 | private $specs; |
||
| 148 | |||
| 149 | /** |
||
| 150 | * A map of all the properties that should be automagically set on all |
||
| 151 | * objects instantiated by the injector |
||
| 152 | */ |
||
| 153 | private $autoProperties; |
||
| 154 | |||
| 155 | /** |
||
| 156 | * A singleton if you want to use it that way |
||
| 157 | * |
||
| 158 | * @var Injector |
||
| 159 | */ |
||
| 160 | private static $instance; |
||
| 161 | |||
| 162 | /** |
||
| 163 | * Indicates whether or not to automatically scan properties in injected objects to auto inject |
||
| 164 | * stuff, similar to the way grails does things. |
||
| 165 | * |
||
| 166 | * @var boolean |
||
| 167 | */ |
||
| 168 | private $autoScanProperties = false; |
||
| 169 | |||
| 170 | /** |
||
| 171 | * The default factory used to create new instances. |
||
| 172 | * |
||
| 173 | * The {@link InjectionCreator} is used by default, which simply directly |
||
| 174 | * creates objects. This can be changed to use a different default creation |
||
| 175 | * method if desired. |
||
| 176 | * |
||
| 177 | * Each individual component can also specify a custom factory to use by |
||
| 178 | * using the `factory` parameter. |
||
| 179 | * |
||
| 180 | * @var Factory |
||
| 181 | */ |
||
| 182 | protected $objectCreator; |
||
| 183 | |||
| 184 | /** |
||
| 185 | * Locator for determining Config properties for services |
||
| 186 | * |
||
| 187 | * @var ServiceConfigurationLocator |
||
| 188 | */ |
||
| 189 | protected $configLocator; |
||
| 190 | |||
| 191 | /** |
||
| 192 | * Create a new injector. |
||
| 193 | * |
||
| 194 | * @param array $config |
||
| 195 | * Service configuration |
||
| 196 | */ |
||
| 197 | public function __construct($config = null) |
||
| 224 | |||
| 225 | /** |
||
| 226 | * The injector instance this one was copied from when Injector::nest() was called. |
||
| 227 | * |
||
| 228 | * @var Injector |
||
| 229 | */ |
||
| 230 | protected $nestedFrom = null; |
||
| 231 | |||
| 232 | /** |
||
| 233 | * If a user wants to use the injector as a static reference |
||
| 234 | * |
||
| 235 | * @param array $config |
||
| 236 | * @return Injector |
||
| 237 | */ |
||
| 238 | public static function inst($config = null) |
||
| 245 | |||
| 246 | /** |
||
| 247 | * Sets the default global injector instance. |
||
| 248 | * |
||
| 249 | * @param Injector $instance |
||
| 250 | * @return Injector Reference to new active Injector instance |
||
| 251 | */ |
||
| 252 | public static function set_inst(Injector $instance) |
||
| 256 | |||
| 257 | /** |
||
| 258 | * Make the newly active {@link Injector} be a copy of the current active |
||
| 259 | * {@link Injector} instance. |
||
| 260 | * |
||
| 261 | * You can then make changes to the injector with methods such as |
||
| 262 | * {@link Injector::inst()->registerService()} which will be discarded |
||
| 263 | * upon a subsequent call to {@link Injector::unnest()} |
||
| 264 | * |
||
| 265 | * @return Injector Reference to new active Injector instance |
||
| 266 | */ |
||
| 267 | public static function nest() |
||
| 275 | |||
| 276 | /** |
||
| 277 | * Change the active Injector back to the Injector instance the current active |
||
| 278 | * Injector object was copied from. |
||
| 279 | * |
||
| 280 | * @return Injector Reference to restored active Injector instance |
||
| 281 | */ |
||
| 282 | public static function unnest() |
||
| 294 | |||
| 295 | /** |
||
| 296 | * Indicate whether we auto scan injected objects for properties to set. |
||
| 297 | * |
||
| 298 | * @param boolean $val |
||
| 299 | */ |
||
| 300 | public function setAutoScanProperties($val) |
||
| 304 | |||
| 305 | /** |
||
| 306 | * Sets the default factory to use for creating new objects. |
||
| 307 | * |
||
| 308 | * @param \SilverStripe\Core\Injector\Factory $obj |
||
| 309 | */ |
||
| 310 | public function setObjectCreator(Factory $obj) |
||
| 314 | |||
| 315 | /** |
||
| 316 | * @return Factory |
||
| 317 | */ |
||
| 318 | public function getObjectCreator() |
||
| 322 | |||
| 323 | /** |
||
| 324 | * Set the configuration locator |
||
| 325 | * @param ServiceConfigurationLocator $configLocator |
||
| 326 | */ |
||
| 327 | public function setConfigLocator($configLocator) |
||
| 331 | |||
| 332 | /** |
||
| 333 | * Retrieve the configuration locator |
||
| 334 | * @return ServiceConfigurationLocator |
||
| 335 | */ |
||
| 336 | public function getConfigLocator() |
||
| 340 | |||
| 341 | /** |
||
| 342 | * Add in a specific mapping that should be catered for on a type. |
||
| 343 | * This allows configuration of what should occur when an object |
||
| 344 | * of a particular type is injected, and what items should be injected |
||
| 345 | * for those properties / methods. |
||
| 346 | * |
||
| 347 | * @param string $class The class to set a mapping for |
||
| 348 | * @param string $property The property to set the mapping for |
||
| 349 | * @param string $toInject The registered type that will be injected |
||
| 350 | * @param string $injectVia Whether to inject by setting a property or calling a setter |
||
| 351 | */ |
||
| 352 | public function setInjectMapping($class, $property, $toInject, $injectVia = 'property') |
||
| 360 | |||
| 361 | /** |
||
| 362 | * Add an object that should be automatically set on managed objects |
||
| 363 | * |
||
| 364 | * This allows you to specify, for example, that EVERY managed object |
||
| 365 | * will be automatically inject with a log object by the following |
||
| 366 | * |
||
| 367 | * $injector->addAutoProperty('log', new Logger()); |
||
| 368 | * |
||
| 369 | * @param string $property |
||
| 370 | * the name of the property |
||
| 371 | * @param object $object |
||
| 372 | * the object to be set |
||
| 373 | * @return $this |
||
| 374 | */ |
||
| 375 | public function addAutoProperty($property, $object) |
||
| 380 | |||
| 381 | /** |
||
| 382 | * Load services using the passed in configuration for those services |
||
| 383 | * |
||
| 384 | * @param array $config |
||
| 385 | * @return $this |
||
| 386 | */ |
||
| 387 | public function load($config = array()) |
||
| 453 | |||
| 454 | /** |
||
| 455 | * Update the configuration of an already defined service |
||
| 456 | * |
||
| 457 | * Use this if you don't want to register a complete new config, just append |
||
| 458 | * to an existing configuration. Helpful to avoid overwriting someone else's changes |
||
| 459 | * |
||
| 460 | * updateSpec('RequestProcessor', 'filters', '%$MyFilter') |
||
| 461 | * |
||
| 462 | * @param string $id |
||
| 463 | * The name of the service to update the definition for |
||
| 464 | * @param string $property |
||
| 465 | * The name of the property to update. |
||
| 466 | * @param mixed $value |
||
| 467 | * The value to set |
||
| 468 | * @param boolean $append |
||
| 469 | * Whether to append (the default) when the property is an array |
||
| 470 | */ |
||
| 471 | public function updateSpec($id, $property, $value, $append = true) |
||
| 489 | |||
| 490 | /** |
||
| 491 | * Update a class specification to convert constructor configuration information if needed |
||
| 492 | * |
||
| 493 | * We do this as a separate process to avoid unneeded calls to convertServiceProperty |
||
| 494 | * |
||
| 495 | * @param array $spec |
||
| 496 | * The class specification to update |
||
| 497 | */ |
||
| 498 | protected function updateSpecConstructor(&$spec) |
||
| 504 | |||
| 505 | /** |
||
| 506 | * Recursively convert a value into its proper representation with service references |
||
| 507 | * resolved to actual objects |
||
| 508 | * |
||
| 509 | * @param string $value |
||
| 510 | * @return array|mixed|string |
||
| 511 | */ |
||
| 512 | public function convertServiceProperty($value) |
||
| 535 | |||
| 536 | /** |
||
| 537 | * Instantiate a managed object |
||
| 538 | * |
||
| 539 | * Given a specification of the form |
||
| 540 | * |
||
| 541 | * array( |
||
| 542 | * 'class' => 'ClassName', |
||
| 543 | * 'properties' => array('property' => 'scalar', 'other' => '%$BeanRef') |
||
| 544 | * 'id' => 'ServiceId', |
||
| 545 | * 'type' => 'singleton|prototype' |
||
| 546 | * ) |
||
| 547 | * |
||
| 548 | * will create a new object, store it in the service registry, and |
||
| 549 | * set any relevant properties |
||
| 550 | * |
||
| 551 | * Optionally, you can pass a class name directly for creation |
||
| 552 | * |
||
| 553 | * To access this from the outside, you should call ->get('Name') to ensure |
||
| 554 | * the appropriate checks are made on the specific type. |
||
| 555 | * |
||
| 556 | * |
||
| 557 | * @param array $spec |
||
| 558 | * The specification of the class to instantiate |
||
| 559 | * @param string $id |
||
| 560 | * The name of the object being created. If not supplied, then the id will be inferred from the |
||
| 561 | * object being created |
||
| 562 | * @param string $type |
||
| 563 | * Whether to create as a singleton or prototype object. Allows code to be explicit as to how it |
||
| 564 | * wants the object to be returned |
||
| 565 | * @return object |
||
| 566 | */ |
||
| 567 | protected function instantiate($spec, $id = null, $type = null) |
||
| 606 | |||
| 607 | /** |
||
| 608 | * Inject $object with available objects from the service cache |
||
| 609 | * |
||
| 610 | * @todo Track all the existing objects that have had a service bound |
||
| 611 | * into them, so we can update that binding at a later point if needbe (ie |
||
| 612 | * if the managed service changes) |
||
| 613 | * |
||
| 614 | * @param object $object |
||
| 615 | * The object to inject |
||
| 616 | * @param string $asType |
||
| 617 | * The ID this item was loaded as. This is so that the property configuration |
||
| 618 | * for a type is referenced correctly in case $object is no longer the same |
||
| 619 | * type as the loaded config specification had it as. |
||
| 620 | */ |
||
| 621 | public function inject($object, $asType = null) |
||
| 759 | |||
| 760 | /** |
||
| 761 | * Helper to set a property's value |
||
| 762 | * |
||
| 763 | * @param object $object |
||
| 764 | * Set an object's property to a specific value |
||
| 765 | * @param string $name |
||
| 766 | * The name of the property to set |
||
| 767 | * @param mixed $value |
||
| 768 | * The value to set |
||
| 769 | */ |
||
| 770 | protected function setObjectProperty($object, $name, $value) |
||
| 778 | |||
| 779 | /** |
||
| 780 | * Does the given service exist, and if so, what's the stored name for it? |
||
| 781 | * |
||
| 782 | * We do a special check here for services that are using compound names. For example, |
||
| 783 | * we might want to say that a property should be injected with Log.File or Log.Memory, |
||
| 784 | * but have only registered a 'Log' service, we'll instead return that. |
||
| 785 | * |
||
| 786 | * Will recursively call hasService for each depth of dotting |
||
| 787 | * |
||
| 788 | * @param string $name |
||
| 789 | * @return string The name of the service (as it might be different from the one passed in) |
||
| 790 | * The name of the service (as it might be different from the one passed in) |
||
| 791 | */ |
||
| 792 | public function hasService($name) |
||
| 807 | |||
| 808 | /** |
||
| 809 | * Register a service object with an optional name to register it as the |
||
| 810 | * service for |
||
| 811 | * |
||
| 812 | * @param object $service The object to register |
||
| 813 | * @param string $replace The name of the object to replace (if different to the |
||
| 814 | * class name of the object to register) |
||
| 815 | */ |
||
| 816 | public function registerService($service, $replace = null) |
||
| 827 | |||
| 828 | /** |
||
| 829 | * Removes a named object from the cached list of objects managed |
||
| 830 | * by the inject |
||
| 831 | * |
||
| 832 | * @param string $name The name to unregister |
||
| 833 | */ |
||
| 834 | public function unregisterNamedObject($name) |
||
| 838 | |||
| 839 | /** |
||
| 840 | * Clear out all objects that are managed by the injetor. |
||
| 841 | */ |
||
| 842 | public function unregisterAllObjects() |
||
| 846 | |||
| 847 | /** |
||
| 848 | * Get a named managed object |
||
| 849 | * |
||
| 850 | * Will first check to see if the item has been registered as a configured service/bean |
||
| 851 | * and return that if so. |
||
| 852 | * |
||
| 853 | * Next, will check to see if there's any registered configuration for the given type |
||
| 854 | * and will then try and load that |
||
| 855 | * |
||
| 856 | * Failing all of that, will just return a new instance of the |
||
| 857 | * specificied object. |
||
| 858 | * |
||
| 859 | * @param string $name |
||
| 860 | * the name of the service to retrieve. If not a registered |
||
| 861 | * service, then a class of the given name is instantiated |
||
| 862 | * @param boolean $asSingleton |
||
| 863 | * Whether to register the created object as a singleton |
||
| 864 | * if no other configuration is found |
||
| 865 | * @param array $constructorArgs |
||
| 866 | * Optional set of arguments to pass as constructor arguments |
||
| 867 | * if this object is to be created from scratch |
||
| 868 | * (ie asSingleton = false) |
||
| 869 | * @return mixed the instance of the specified object |
||
| 870 | */ |
||
| 871 | public function get($name, $asSingleton = true, $constructorArgs = null) |
||
| 926 | |||
| 927 | /** |
||
| 928 | * Magic method to return an item directly |
||
| 929 | * |
||
| 930 | * @param string $name |
||
| 931 | * The named object to retrieve |
||
| 932 | * @return mixed |
||
| 933 | */ |
||
| 934 | public function __get($name) |
||
| 938 | |||
| 939 | /** |
||
| 940 | * Similar to get() but always returns a new object of the given type |
||
| 941 | * |
||
| 942 | * Additional parameters are passed through as |
||
| 943 | * |
||
| 944 | * @param string $name |
||
| 945 | * @param mixed $argument,... arguments to pass to the constructor |
||
| 946 | * @return mixed A new instance of the specified object |
||
| 947 | */ |
||
| 948 | public function create($name, $argument = null) |
||
| 954 | |||
| 955 | /** |
||
| 956 | * Creates an object with the supplied argument array |
||
| 957 | * |
||
| 958 | * @param string $name Name of the class to create an object of |
||
| 959 | * @param array $constructorArgs Arguments to pass to the constructor |
||
| 960 | * @return mixed |
||
| 961 | */ |
||
| 962 | public function createWithArgs($name, $constructorArgs) |
||
| 966 | } |
||
| 967 |
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.