Skip to content
This repository
Browse code

API CHANGE: Don't have any instance caching in singleton(), rely on I…

…njector for this.
  • Loading branch information...
commit 114ebb695342e0ebdd2acd8c9240e3f79cea6027 1 parent f65a7c6
Sam Minnée authored June 11, 2012
9  core/Core.php
@@ -365,15 +365,10 @@ function getClassFile($className) {
365 365
  * @return Object
366 366
  */
367 367
 function singleton($className) {
368  
-	global $_SINGLETONS;
  368
+	if($className == "Config") user_error("Don't pass Config to singleton()", E_USER_ERROR);
369 369
 	if(!isset($className)) user_error("singleton() Called without a class", E_USER_ERROR);
370 370
 	if(!is_string($className)) user_error("singleton() passed bad class_name: " . var_export($className,true), E_USER_ERROR);
371  
-	if(!isset($_SINGLETONS[$className])) {
372  
-		if(!class_exists($className)) user_error("Bad class to singleton() - $className", E_USER_ERROR);
373  
-		$_SINGLETONS[$className] = Injector::inst()->get($className);
374  
-		if(!$_SINGLETONS[$className]) user_error("singleton() Unknown class '$className'", E_USER_ERROR);
375  
-	}
376  
-	return $_SINGLETONS[$className];
  371
+	return Injector::inst()->get($className);
377 372
 }
378 373
 
379 374
 function project() {
5  core/Object.php
@@ -466,6 +466,7 @@ public static function add_extension($class, $extension) {
466 466
 		}
467 467
 
468 468
 		Config::inst()->update($class, 'extensions', array($extension));
  469
+		Injector::inst()->unregisterAllObjects();
469 470
 
470 471
 		// load statics now for DataObject classes
471 472
 		if(is_subclass_of($class, 'DataObject')) {
@@ -496,8 +497,7 @@ public static function remove_extension($class, $extension) {
496 497
 		Config::inst()->remove($class, 'extensions', Config::anything(), $extension);
497 498
 
498 499
 		// unset singletons to avoid side-effects
499  
-		global $_SINGLETONS;
500  
-		$_SINGLETONS = array();
  500
+		Injector::inst()->unregisterAllObjects();
501 501
 
502 502
 		// unset some caches
503 503
 		$subclasses = ClassInfo::subclassesFor($class);
@@ -541,7 +541,6 @@ public function __construct() {
541 541
 		
542 542
 		if($extensionClasses = ClassInfo::ancestry($this->class)) foreach($extensionClasses as $class) {
543 543
 			if(in_array($class, $notExtendable)) continue;
544  
-
545 544
 			if($extensions = Config::inst()->get($class, 'extensions', Config::UNINHERITED)) {
546 545
 				foreach($extensions as $extension) {
547 546
 					// Get the extension class for this extension
5  dev/SapphireTest.php
@@ -299,8 +299,7 @@ function setUpOnce() {
299 299
 		}
300 300
 		// clear singletons, they're caching old extension info 
301 301
 		// which is used in DatabaseAdmin->doBuild()
302  
-		global $_SINGLETONS;
303  
-		$_SINGLETONS = array();
  302
+		Injector::inst()->unregisterAllObjects();
304 303
 
305 304
 		// Set default timezone consistently to avoid NZ-specific dependencies
306 305
 		date_default_timezone_set('UTC');
@@ -789,9 +788,7 @@ static function delete_all_temp_dbs() {
789 788
 	function resetDBSchema($includeExtraDataObjects = false) {
790 789
 		if(self::using_temp_db()) {
791 790
 			// clear singletons, they're caching old extension info which is used in DatabaseAdmin->doBuild()
792  
-			global $_SINGLETONS;
793 791
 			Injector::inst()->unregisterAllObjects();
794  
-			$_SINGLETONS = array();
795 792
 
796 793
 			$dataClasses = ClassInfo::subclassesFor('DataObject');
797 794
 			array_shift($dataClasses);
7  model/DataQuery.php
@@ -123,7 +123,8 @@ function initialiseQuery() {
123 123
 
124 124
 		$this->query->setFrom("\"$baseClass\"");
125 125
 
126  
-		singleton($this->dataClass)->extend('augmentDataQueryCreation', $this->query, $this);
  126
+		$obj = Injector::inst()->get($baseClass);
  127
+		$obj->extend('augmentDataQueryCreation', $this->query, $this);
127 128
 	}
128 129
 
129 130
 	function setQueriedColumns($queriedColumns) {
@@ -225,7 +226,9 @@ function getFinalisedQuery($queriedColumns = null) {
225 226
 		$query->selectField("CASE WHEN \"$baseClass\".\"ClassName\" IS NOT NULL THEN \"$baseClass\".\"ClassName\" ELSE '$baseClass' END", "RecordClassName");
226 227
 
227 228
 		// TODO: Versioned, Translatable, SiteTreeSubsites, etc, could probably be better implemented as subclasses of DataQuery
228  
-		singleton($this->dataClass)->extend('augmentSQL', $query, $this);
  229
+
  230
+		$obj = Injector::inst()->get(ClassInfo::baseDataClass($this->dataClass));
  231
+		$obj->extend('augmentSQL', $query, $this);
229 232
 
230 233
 		$this->ensureSelectContainsOrderbyColumns($query);
231 234
 
4  tests/core/ObjectTest.php
@@ -11,9 +11,7 @@ class ObjectTest extends SapphireTest {
11 11
 	
12 12
 	function setUp() {
13 13
 		parent::setUp();
14  
-		
15  
-		global $_SINGLETONS;
16  
-		$_SINGLETONS = array();
  14
+		Injector::inst()->unregisterAllObjects();
17 15
 	}
18 16
 	
19 17
 	function testHasmethodBehaviour() {

0 notes on commit 114ebb6

nyeholt

What are the 'side-effects' that need a clearing out of all the objects here (and in add_extension)?

I'm concerned this could have some potentially nasty side-effects itself; the case I'm thinking of is

  • ObjectA is bound to singletonA
  • ObjectB is bound to singletonA
  • add_extension clears the injector of singletonA
  • ObjectC is bound to singletonA
  • Now ObjectA->singletonA !== ObjectC->singletonA

As far as I can tell off the top of my head this doesn't cause any problems at the moment, but it's something that could cause some headscratching for someone down the line. Clearing all objects feels like a work around for something occurring instead of solving the root cause

Andrew Short

I've run into some pretty big problems with this - if you register a named service, it's cleared every time an extension is added. What were the reasons for adding this?

Sam Minnée

The problem was that the cached object didn't have the relevant extension. We probably need a way of regenerating the created object without removing registered objects. Alternatively, the add_extension call could update the cached instance in the injector, but that might get weird.

Another way of side-stepping the problem would be to get pickier about when add_extension could be called, and ensure that extensions were applied before other activities such as injector registrations.

We're still going to have a catch-22 if you register an specific object instance and then add an extension to that object's class. Forcing extensions to be applied prior to everything else would side-step that.

nyeholt

Alternatively, the add_extension call could update the cached instance in the injector, but that might get weird.

Without digging deeper into the murk of how extensions are managed right now, I'd see that as the more appropriate way of approaching things in the interim.

With extensions in general, is it common to have add_extension being called during application execution as opposed to during the bootstrapping process via _config (or triggered from a method call in _config)?

Hamish Friedlander
Please sign in to comment.
Something went wrong with that request. Please try again.