Completed
Push — master ( 90072e...c81959 )
by Daniel
11:23
created

InjectorTest::testConstantUsage()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 18
nc 1
nop 0
dl 0
loc 28
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Core\Tests\Injector;
4
5
use InvalidArgumentException;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Core\Injector\Injector;
8
use SilverStripe\Core\Injector\SilverStripeServiceConfigurationLocator;
9
use SilverStripe\Core\Tests\Injector\AopProxyServiceTest\AnotherService;
10
use SilverStripe\Core\Tests\Injector\AopProxyServiceTest\SampleService;
11
use SilverStripe\Core\Tests\Injector\InjectorTest\CircularOne;
12
use SilverStripe\Core\Tests\Injector\InjectorTest\CircularTwo;
13
use SilverStripe\Core\Tests\Injector\InjectorTest\ConstructableObject;
14
use SilverStripe\Core\Tests\Injector\InjectorTest\DummyRequirements;
15
use SilverStripe\Core\Tests\Injector\InjectorTest\MyChildClass;
16
use SilverStripe\Core\Tests\Injector\InjectorTest\MyParentClass;
17
use SilverStripe\Core\Tests\Injector\InjectorTest\NeedsBothCirculars;
18
use SilverStripe\Core\Tests\Injector\InjectorTest\NewRequirementsBackend;
19
use SilverStripe\Core\Tests\Injector\InjectorTest\OriginalRequirementsBackend;
20
use SilverStripe\Core\Tests\Injector\InjectorTest\OtherTestObject;
21
use SilverStripe\Core\Tests\Injector\InjectorTest\TestObject;
22
use SilverStripe\Core\Tests\Injector\InjectorTest\TestSetterInjections;
23
use SilverStripe\Core\Tests\Injector\InjectorTest\TestStaticInjections;
24
use SilverStripe\Dev\SapphireTest;
25
use stdClass;
26
27
define('TEST_SERVICES', __DIR__ . '/AopProxyServiceTest');
28
29
/**
30
 * Tests for the dependency injector
31
 *
32
 * Note that these are SS conversions of the existing Simpletest unit tests
33
 *
34
 * @author      [email protected]
35
 * @license     BSD License http://silverstripe.org/bsd-license/
36
 * @skipUpgrade
37
 */
38
class InjectorTest extends SapphireTest
39
{
40
41
    protected $nestingLevel = 0;
42
43
    public function setUp()
44
    {
45
        parent::setUp();
46
47
        $this->nestingLevel = 0;
48
    }
49
50
    public function tearDown()
51
    {
52
53
        while ($this->nestingLevel > 0) {
54
            $this->nestingLevel--;
55
            Config::unnest();
56
        }
57
58
        parent::tearDown();
59
    }
60
61
    public function testCorrectlyInitialised()
62
    {
63
        $injector = Injector::inst();
64
        $this->assertTrue(
65
            $injector->getConfigLocator() instanceof SilverStripeServiceConfigurationLocator,
66
            'Failure most likely because the injector has been referenced BEFORE being initialised in Core.php'
67
        );
68
    }
69
70
    public function testBasicInjector()
71
    {
72
        $injector = new Injector();
73
        $injector->setAutoScanProperties(true);
74
        $config = array(
75
            'SampleService' => array(
76
                'src' => TEST_SERVICES . '/SampleService.php',
77
                'class' => SampleService::class,
78
            )
79
        );
80
81
        $injector->load($config);
82
        $this->assertEquals(
83
            'SampleService',
84
            $injector->hasService('SampleService')
85
        );
86
87
        $myObject = new TestObject();
88
        $injector->inject($myObject);
89
90
        $this->assertInstanceOf(
91
            SampleService::class,
92
            $myObject->sampleService
93
        );
94
    }
95
96
    public function testConfiguredInjector()
97
    {
98
        $injector = new Injector();
99
        $services = array(
100
            'AnotherService' => array(
101
                'class' => AnotherService::class,
102
                'src' => TEST_SERVICES . '/AnotherService.php',
103
                'properties' => array('config_property' => 'Value'),
104
            ),
105
            'SampleService' => array(
106
                'class' => SampleService::class,
107
                'src' => TEST_SERVICES . '/SampleService.php',
108
            )
109
        );
110
111
        $injector->load($services);
112
        $this->assertEquals(
113
            'SampleService',
114
            $injector->hasService('SampleService')
115
        );
116
        // We expect a false because the AnotherService::class is actually
117
        // just a replacement of the SilverStripe\Core\Tests\Injector\AopProxyServiceTest\SampleService
118
        $this->assertEquals(
119
            'AnotherService',
120
            $injector->hasService('AnotherService')
121
        );
122
123
        $item = $injector->get('AnotherService');
124
125
        $this->assertEquals('Value', $item->config_property);
126
    }
127
128
    public function testIdToNameMap()
129
    {
130
        $injector = new Injector();
131
        $services = array(
132
            'FirstId' => AnotherService::class,
133
            'SecondId' => SampleService::class,
134
        );
135
136
        $injector->load($services);
137
138
        $this->assertTrue($injector->hasService('FirstId') == 'FirstId');
139
        $this->assertTrue($injector->hasService('SecondId') == 'SecondId');
140
141
        $this->assertTrue($injector->get('FirstId') instanceof AnotherService);
142
        $this->assertTrue($injector->get('SecondId') instanceof SampleService);
143
    }
144
145
    public function testReplaceService()
146
    {
147
        $injector = new Injector();
148
        $injector->setAutoScanProperties(true);
149
150
        $config = array(
151
            'SampleService' => array(
152
                'src' => TEST_SERVICES . '/SampleService.php',
153
                'class' => SampleService::class,
154
            )
155
        );
156
157
        // load
158
        $injector->load($config);
159
160
        // inject
161
        $myObject = new TestObject();
162
        $injector->inject($myObject);
163
164
        $this->assertInstanceOf(
165
            SampleService::class,
166
            $myObject->sampleService
167
        );
168
169
        // also tests that ID can be the key in the array
170
        $config = array(
171
            'SampleService' => array(
172
                'src' => TEST_SERVICES . '/AnotherService.php',
173
                'class' => AnotherService::class,
174
            )
175
        );
176
        // , 'id' => SampleService::class));
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
177
        // load
178
        $injector->load($config);
179
180
        $injector->inject($myObject);
181
        $this->assertInstanceOf(
182
            AnotherService::class,
183
            $myObject->sampleService
184
        );
185
    }
186
187
    public function testUpdateSpec()
188
    {
189
        $injector = new Injector();
190
        $services = array(
191
            AnotherService::class => array(
192
                'src' => TEST_SERVICES . '/AnotherService.php',
193
                'properties' => array(
194
                    'filters' => array(
195
                        'One',
196
                        'Two',
197
                    )
198
                ),
199
            )
200
        );
201
202
        $injector->load($services);
203
204
        $injector->updateSpec(AnotherService::class, 'filters', 'Three');
205
        $another = $injector->get(AnotherService::class);
206
207
        $this->assertEquals(3, count($another->filters));
208
        $this->assertEquals('Three', $another->filters[2]);
209
    }
210
211
    public function testConstantUsage()
212
    {
213
        $injector = new Injector();
214
        $services = array(
215
            AnotherService::class => array(
216
                'properties' => array(
217
                    'filters' => array(
218
                        '`BASE_PATH`',
219
                        '`TEMP_FOLDER`',
220
                        '`NOT_DEFINED`',
221
                        'THIRDPARTY_DIR' // Not back-tick escaped
222
                    )
223
                ),
224
            )
225
        );
226
227
        $injector->load($services);
228
        $another = $injector->get(AnotherService::class);
229
        $this->assertEquals(
230
            [
231
                BASE_PATH,
232
                TEMP_FOLDER,
233
                null,
234
                'THIRDPARTY_DIR',
235
            ],
236
            $another->filters
237
        );
238
    }
239
240
    public function testAutoSetInjector()
241
    {
242
        $injector = new Injector();
243
        $injector->setAutoScanProperties(true);
244
        $injector->addAutoProperty('auto', 'somevalue');
245
        $config = array(
246
            'SampleService' => array(
247
                'src' => TEST_SERVICES . '/SampleService.php',
248
                'class' => SampleService::class
249
            )
250
        );
251
        $injector->load($config);
252
253
        $this->assertEquals(
254
            'SampleService',
255
            $injector->hasService('SampleService')
256
        );
257
        // We expect a false because the AnotherService::class is actually
258
        // just a replacement of the SilverStripe\Core\Tests\Injector\AopProxyServiceTest\SampleService
259
260
        $myObject = new InjectorTest\TestObject();
261
262
        $injector->inject($myObject);
263
264
        $this->assertInstanceOf(
265
            SampleService::class,
266
            $myObject->sampleService
267
        );
268
        $this->assertEquals($myObject->auto, 'somevalue');
0 ignored issues
show
Bug introduced by
The property auto does not seem to exist in SilverStripe\Core\Tests\...InjectorTest\TestObject.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
269
    }
270
271
    public function testSettingSpecificProperty()
272
    {
273
        $injector = new Injector();
274
        $config = array(AnotherService::class);
275
        $injector->load($config);
276
        $injector->setInjectMapping(TestObject::class, 'sampleService', AnotherService::class);
277
        $testObject = $injector->get(TestObject::class);
278
279
        $this->assertInstanceOf(
280
            AnotherService::class,
281
            $testObject->sampleService
282
        );
283
    }
284
285
    public function testSettingSpecificMethod()
286
    {
287
        $injector = new Injector();
288
        $config = array(AnotherService::class);
289
        $injector->load($config);
290
        $injector->setInjectMapping(TestObject::class, 'setSomething', AnotherService::class, 'method');
291
292
        $testObject = $injector->get(TestObject::class);
293
294
        $this->assertInstanceOf(
295
            AnotherService::class,
296
            $testObject->sampleService
297
        );
298
    }
299
300
    public function testInjectingScopedService()
301
    {
302
        $injector = new Injector();
303
304
        $config = array(
305
            AnotherService::class,
306
            'SilverStripe\Core\Tests\Injector\AopProxyServiceTest\AnotherService.DottedChild'   => SampleService::class,
307
        );
308
309
        $injector->load($config);
310
311
        $service = $injector->get('SilverStripe\Core\Tests\Injector\AopProxyServiceTest\AnotherService.DottedChild');
312
        $this->assertInstanceOf(SampleService::class, $service);
313
314
        $service = $injector->get('SilverStripe\Core\Tests\Injector\AopProxyServiceTest\AnotherService.Subset');
315
        $this->assertInstanceOf(AnotherService::class, $service);
316
317
        $injector->setInjectMapping(TestObject::class, 'sampleService', 'SilverStripe\Core\Tests\Injector\AopProxyServiceTest\AnotherService.Geronimo');
318
        $testObject = $injector->create(TestObject::class);
319
        $this->assertEquals(get_class($testObject->sampleService), AnotherService::class);
320
321
        $injector->setInjectMapping(TestObject::class, 'sampleService', 'SilverStripe\Core\Tests\Injector\AopProxyServiceTest\AnotherService.DottedChild.AnotherDown');
322
        $testObject = $injector->create(TestObject::class);
323
        $this->assertEquals(get_class($testObject->sampleService), SampleService::class);
324
    }
325
326
    public function testInjectUsingConstructor()
327
    {
328
        $injector = new Injector();
329
        $config = array(
330
            'SampleService' => array(
331
                'src' => TEST_SERVICES . '/SampleService.php',
332
                'class' => SampleService::class,
333
                'constructor' => array(
334
                    'val1',
335
                    'val2',
336
                )
337
            )
338
        );
339
340
        $injector->load($config);
341
        $sample = $injector->get('SampleService');
342
        $this->assertEquals($sample->constructorVarOne, 'val1');
343
        $this->assertEquals($sample->constructorVarTwo, 'val2');
344
345
        $injector = new Injector();
346
        $config = array(
347
            'AnotherService' => AnotherService::class,
348
            'SampleService' => array(
349
                'src' => TEST_SERVICES . '/SampleService.php',
350
                'class' => SampleService::class,
351
                'constructor' => array(
352
                    'val1',
353
                    '%$AnotherService',
354
                )
355
            )
356
        );
357
358
        $injector->load($config);
359
        $sample = $injector->get('SampleService');
360
        $this->assertEquals($sample->constructorVarOne, 'val1');
361
        $this->assertInstanceOf(
362
            AnotherService::class,
363
            $sample->constructorVarTwo
364
        );
365
366
        $injector = new Injector();
367
        $config = array(
368
            'SampleService' => array(
369
                'src' => TEST_SERVICES . '/SampleService.php',
370
                'class' => SampleService::class,
371
                'constructor' => array(
372
                    'val1',
373
                    'val2',
374
                )
375
            )
376
        );
377
378
        $injector->load($config);
379
        $sample = $injector->get('SampleService');
380
        $this->assertEquals($sample->constructorVarOne, 'val1');
381
        $this->assertEquals($sample->constructorVarTwo, 'val2');
382
383
        // test constructors on prototype
384
        $injector = new Injector();
385
        $config = array(
386
            'SampleService' => array(
387
                'type'  => 'prototype',
388
                'src' => TEST_SERVICES . '/SampleService.php',
389
                'class' => SampleService::class,
390
                'constructor' => array(
391
                    'val1',
392
                    'val2',
393
                )
394
            )
395
        );
396
397
        $injector->load($config);
398
        $sample = $injector->get('SampleService');
399
        $this->assertEquals($sample->constructorVarOne, 'val1');
400
        $this->assertEquals($sample->constructorVarTwo, 'val2');
401
402
        $again = $injector->get('SampleService');
403
        $this->assertFalse($sample === $again);
404
405
        $this->assertEquals($sample->constructorVarOne, 'val1');
406
        $this->assertEquals($sample->constructorVarTwo, 'val2');
407
    }
408
409
    public function testInjectUsingSetter()
410
    {
411
        $injector = new Injector();
412
        $injector->setAutoScanProperties(true);
413
        $config = array(
414
            'SampleService' => array(
415
                'src' => TEST_SERVICES . '/SampleService.php',
416
                'class' => SampleService::class,
417
            )
418
        );
419
420
        $injector->load($config);
421
        $this->assertEquals('SampleService', $injector->hasService('SampleService'));
422
423
        $myObject = new InjectorTest\OtherTestObject();
424
        $injector->inject($myObject);
425
426
        $this->assertInstanceOf(
427
            SampleService::class,
428
            $myObject->s()
429
        );
430
431
        // and again because it goes down a different code path when setting things
432
        // based on the inject map
433
        $myObject = new InjectorTest\OtherTestObject();
434
        $injector->inject($myObject);
435
436
        $this->assertInstanceOf(
437
            SampleService::class,
438
            $myObject->s()
439
        );
440
    }
441
442
    // make sure we can just get any arbitrary object - it should be created for us
443
    public function testInstantiateAnObjectViaGet()
444
    {
445
        $injector = new Injector();
446
        $injector->setAutoScanProperties(true);
447
        $config = array(
448
            'SampleService' => array(
449
                'src' => TEST_SERVICES . '/SampleService.php',
450
                'class' => SampleService::class,
451
            )
452
        );
453
454
        $injector->load($config);
455
        $this->assertEquals('SampleService', $injector->hasService('SampleService'));
456
457
        $myObject = $injector->get(OtherTestObject::class);
458
        $this->assertInstanceOf(
459
            SampleService::class,
460
            $myObject->s()
461
        );
462
463
        // and again because it goes down a different code path when setting things
464
        // based on the inject map
465
        $myObject = $injector->get(OtherTestObject::class);
466
        $this->assertInstanceOf(SampleService::class, $myObject->s());
467
    }
468
469
    public function testCircularReference()
470
    {
471
        $services = array(
472
            'CircularOne' => CircularOne::class,
473
            'CircularTwo' => CircularTwo::class
474
        );
475
        $injector = new Injector($services);
476
        $injector->setAutoScanProperties(true);
477
478
        $obj = $injector->get(NeedsBothCirculars::class);
479
480
        $this->assertTrue($obj->circularOne instanceof InjectorTest\CircularOne);
481
        $this->assertTrue($obj->circularTwo instanceof InjectorTest\CircularTwo);
482
    }
483
484
    public function testPrototypeObjects()
485
    {
486
        $services = array(
487
            'CircularOne' => CircularOne::class,
488
            'CircularTwo' => CircularTwo::class,
489
            'NeedsBothCirculars' => array(
490
                'class' => NeedsBothCirculars::class,
491
                'type' => 'prototype'
492
            )
493
        );
494
        $injector = new Injector($services);
495
        $injector->setAutoScanProperties(true);
496
        $obj1 = $injector->get('NeedsBothCirculars');
497
        $obj2 = $injector->get('NeedsBothCirculars');
498
499
        // if this was the same object, then $obj1->var would now be two
500
        $obj1->var = 'one';
501
        $obj2->var = 'two';
502
503
        $this->assertTrue($obj1->circularOne instanceof InjectorTest\CircularOne);
504
        $this->assertTrue($obj1->circularTwo instanceof InjectorTest\CircularTwo);
505
506
        $this->assertEquals($obj1->circularOne, $obj2->circularOne);
507
        $this->assertNotEquals($obj1, $obj2);
508
    }
509
510
    public function testSimpleInstantiation()
511
    {
512
        $services = array(
513
            'CircularOne' => CircularOne::class,
514
            'CircularTwo' => CircularTwo::class
515
        );
516
        $injector = new Injector($services);
517
518
        // similar to the above, but explicitly instantiating this object here
519
        $obj1 = $injector->create(NeedsBothCirculars::class);
520
        $obj2 = $injector->create(NeedsBothCirculars::class);
521
522
        // if this was the same object, then $obj1->var would now be two
523
        $obj1->var = 'one';
524
        $obj2->var = 'two';
525
526
        $this->assertEquals($obj1->circularOne, $obj2->circularOne);
527
        $this->assertNotEquals($obj1, $obj2);
528
    }
529
530
    public function testCreateWithConstructor()
531
    {
532
        $injector = new Injector();
533
        $obj = $injector->create(CircularTwo::class, 'param');
534
        $this->assertEquals($obj->otherVar, 'param');
535
    }
536
537
    public function testSimpleSingleton()
538
    {
539
        $injector = new Injector();
540
541
        $one = $injector->create(CircularOne::class);
542
        $two = $injector->create(CircularOne::class);
543
544
        $this->assertFalse($one === $two);
545
546
        $one = $injector->get(CircularTwo::class);
547
        $two = $injector->get(CircularTwo::class);
548
549
        $this->assertTrue($one === $two);
550
    }
551
552
    public function testOverridePriority()
553
    {
554
        $injector = new Injector();
555
        $injector->setAutoScanProperties(true);
556
        $config = array(
557
            'SampleService' => array(
558
                'src' => TEST_SERVICES . '/SampleService.php',
559
                'class' => SampleService::class,
560
                'priority' => 10,
561
            )
562
        );
563
564
        // load
565
        $injector->load($config);
566
567
        // inject
568
        $myObject = new InjectorTest\TestObject();
569
        $injector->inject($myObject);
570
571
        $this->assertInstanceOf(SampleService::class, $myObject->sampleService);
572
573
        $config = array(
574
            array(
575
                'src' => TEST_SERVICES . '/AnotherService.php',
576
                'class' => AnotherService::class,
577
                'id' => 'SampleService',
578
                'priority' => 1,
579
            )
580
        );
581
        // load
582
        $injector->load($config);
583
584
        $injector->inject($myObject);
585
        $this->assertInstanceOf(
586
            SampleService::class,
587
            $myObject->sampleService
588
        );
589
    }
590
591
    /**
592
     * Specific test method to illustrate various ways of setting a requirements backend
593
     */
594
    public function testRequirementsSettingOptions()
595
    {
596
        $injector = new Injector();
597
        $config = array(
598
            OriginalRequirementsBackend::class,
599
            NewRequirementsBackend::class,
600
            DummyRequirements::class => array(
601
                'constructor' => array(
602
                    '%$'.OriginalRequirementsBackend::class
603
                )
604
            )
605
        );
606
607
        $injector->load($config);
608
609
        $requirements = $injector->get(DummyRequirements::class);
610
        $this->assertInstanceOf(
611
            OriginalRequirementsBackend::class,
612
            $requirements->backend
613
        );
614
615
        // just overriding the definition here
616
        $injector->load(
617
            array(
618
            DummyRequirements::class => array(
619
                'constructor' => array(
620
                    '%$'.NewRequirementsBackend::class
621
                )
622
            )
623
            )
624
        );
625
626
        // requirements should have been reinstantiated with the new bean setting
627
        $requirements = $injector->get(DummyRequirements::class);
628
        $this->assertInstanceOf(
629
            NewRequirementsBackend::class,
630
            $requirements->backend
631
        );
632
    }
633
634
    /**
635
     * disabled for now
636
     */
637
    public function testStaticInjections()
638
    {
639
        $injector = new Injector();
640
        $config = array(
641
            NewRequirementsBackend::class,
642
        );
643
644
        $injector->load($config);
645
646
        $si = $injector->get(TestStaticInjections::class);
647
        $this->assertInstanceOf(
648
            NewRequirementsBackend::class,
649
            $si->backend
650
        );
651
    }
652
653
    public function testSetterInjections()
654
    {
655
        $injector = new Injector();
656
        $config = array(
657
            NewRequirementsBackend::class,
658
        );
659
660
        $injector->load($config);
661
662
        $si = $injector->get(TestSetterInjections::class);
663
        $this->assertInstanceOf(
664
            NewRequirementsBackend::class,
665
            $si->getBackend()
666
        );
667
    }
668
669
    public function testCustomObjectCreator()
670
    {
671
        $injector = new Injector();
672
        $injector->setObjectCreator(new InjectorTest\SSObjectCreator($injector));
673
        $config = array(
674
            OriginalRequirementsBackend::class,
675
            DummyRequirements::class => array(
676
                'class' => DummyRequirements::class.'(\'%$'.OriginalRequirementsBackend::class.'\')'
677
            )
678
        );
679
        $injector->load($config);
680
681
        $requirements = $injector->get(DummyRequirements::class);
682
        $this->assertEquals(OriginalRequirementsBackend::class, get_class($requirements->backend));
683
    }
684
685
    public function testInheritedConfig()
686
    {
687
688
        // Test that child class does not automatically inherit config
689
        $injector = new Injector(array('locator' => SilverStripeServiceConfigurationLocator::class));
690
        Config::inst()->update(
691
            Injector::class,
692
            MyParentClass::class,
693
            [
694
            'properties' => ['one' => 'the one'],
695
            'class' => MyParentClass::class,
696
            ]
697
        );
698
        $obj = $injector->get(MyParentClass::class);
699
        $this->assertInstanceOf(MyParentClass::class, $obj);
700
        $this->assertEquals($obj->one, 'the one');
701
702
        // Class isn't inherited and parent properties are ignored
703
        $obj = $injector->get(MyChildClass::class);
704
        $this->assertInstanceOf(MyChildClass::class, $obj);
705
        $this->assertNotEquals($obj->one, 'the one');
706
707
        // Set child class as alias
708
        $injector = new Injector(
709
            array(
710
            'locator' => SilverStripeServiceConfigurationLocator::class
711
            )
712
        );
713
        Config::inst()->update(
714
            Injector::class,
715
            MyChildClass::class,
716
            '%$'.MyParentClass::class
717
        );
718
719
        // Class isn't inherited and parent properties are ignored
720
        $obj = $injector->get(MyChildClass::class);
721
        $this->assertInstanceOf(MyParentClass::class, $obj);
722
        $this->assertEquals($obj->one, 'the one');
723
    }
724
725
    public function testSameNamedSingeltonPrototype()
726
    {
727
        $injector = new Injector();
728
729
        // get a singleton object
730
        $object = $injector->get(NeedsBothCirculars::class);
731
        $object->var = 'One';
732
733
        $again = $injector->get(NeedsBothCirculars::class);
734
        $this->assertEquals($again->var, 'One');
735
736
        // create a NEW instance object
737
        $new = $injector->create(NeedsBothCirculars::class);
738
        $this->assertNull($new->var);
739
740
        // this will trigger a problem below
741
        $new->var = 'Two';
742
743
        $again = $injector->get(NeedsBothCirculars::class);
744
        $this->assertEquals($again->var, 'One');
745
    }
746
747
    public function testConvertServicePropertyOnCreate()
748
    {
749
        // make sure convert service property is not called on direct calls to create, only on configured
750
        // declarations to avoid un-needed function calls
751
        $injector = new Injector();
752
        $item = $injector->create(ConstructableObject::class, '%$'.TestObject::class);
753
        $this->assertEquals('%$'.TestObject::class, $item->property);
754
755
        // do it again but have test object configured as a constructor dependency
756
        $injector = new Injector();
757
        $config = array(
758
            ConstructableObject::class => array(
759
                'constructor' => array(
760
                    '%$'.TestObject::class
761
                )
762
            )
763
        );
764
765
        $injector->load($config);
766
        $item = $injector->get(ConstructableObject::class);
767
        $this->assertTrue($item->property instanceof InjectorTest\TestObject);
768
769
        // and with a configured object defining TestObject to be something else!
770
        $injector = new Injector(array('locator' => InjectorTest\InjectorTestConfigLocator::class));
771
        $config = array(
772
            ConstructableObject::class => array(
773
                'constructor' => array(
774
                    '%$'.TestObject::class
775
                )
776
            ),
777
        );
778
779
        $injector->load($config);
780
        $item = $injector->get(ConstructableObject::class);
781
        $this->assertTrue($item->property instanceof InjectorTest\ConstructableObject);
782
783
        $this->assertInstanceOf(OtherTestObject::class, $item->property->property);
784
    }
785
786
    public function testNamedServices()
787
    {
788
        $injector = new Injector();
789
        $service  = new stdClass();
790
791
        $injector->registerService($service, 'NamedService');
792
        $this->assertEquals($service, $injector->get('NamedService'));
793
    }
794
795
    public function testCreateConfiggedObjectWithCustomConstructorArgs()
796
    {
797
        // need to make sure that even if the config defines some constructor params,
798
        // that we take our passed in constructor args instead
799
        $injector = new Injector(array('locator' => InjectorTest\InjectorTestConfigLocator::class));
800
801
        $item = $injector->create('ConfigConstructor', 'othervalue');
802
        $this->assertEquals($item->property, 'othervalue');
803
    }
804
805
    /**
806
     * Tests creating a service with a custom factory.
807
     */
808
    public function testCustomFactory()
809
    {
810
        $injector = new Injector(
811
            array(
812
            'service' => array('factory' => 'factory', 'constructor' => array(1, 2, 3))
813
            )
814
        );
815
816
        $factory = $this->getMock('SilverStripe\\Core\\Injector\\Factory');
817
        $factory
818
            ->expects($this->once())
819
            ->method('create')
820
            ->with($this->equalTo('service'), $this->equalTo(array(1, 2, 3)))
821
            ->will(
822
                $this->returnCallback(
823
                    function ($args) {
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
824
                        return new InjectorTest\TestObject();
825
                    }
826
                )
827
            );
828
829
        $injector->registerService($factory, 'factory');
830
831
        $this->assertInstanceOf(TestObject::class, $injector->get('service'));
832
    }
833
834
    public function testMethods()
835
    {
836
        // do it again but have test object configured as a constructor dependency
837
        $injector = new Injector();
838
        $config = array(
839
            'A' => array(
840
                'class' => TestObject::class,
841
            ),
842
            'B' => array(
843
                'class' => TestObject::class,
844
            ),
845
            'TestService' => array(
846
                'class' => TestObject::class,
847
                'calls' => array(
848
                    array('myMethod', array('%$A')),
849
                    array('myMethod', array('%$B')),
850
                    array('noArgMethod')
851
                )
852
            )
853
        );
854
855
        $injector->load($config);
856
        $item = $injector->get('TestService');
857
        $this->assertTrue($item instanceof InjectorTest\TestObject);
858
        $this->assertEquals(
859
            array($injector->get('A'), $injector->get('B'), 'noArgMethod called'),
860
            $item->methodCalls
861
        );
862
    }
863
864
    /**
865
     * @expectedException InvalidArgumentException
866
     */
867
    public function testNonExistentMethods()
868
    {
869
        $injector = new Injector();
870
        $config = array(
871
            'TestService' => array(
872
                'class' => TestObject::class,
873
                'calls' => array(
874
                    array('thisDoesntExist')
875
                )
876
            )
877
        );
878
879
        $injector->load($config);
880
        $item = $injector->get('TestService');
881
    }
882
883
    /**
884
     * @expectedException InvalidArgumentException
885
     */
886
    public function testProtectedMethods()
887
    {
888
        $injector = new Injector();
889
        $config = array(
890
            'TestService' => array(
891
                'class' => TestObject::class,
892
                'calls' => array(
893
                    array('protectedMethod')
894
                )
895
            )
896
        );
897
898
        $injector->load($config);
899
        $item = $injector->get('TestService');
900
    }
901
902
    /**
903
     * @expectedException InvalidArgumentException
904
     */
905
    public function testTooManyArrayValues()
906
    {
907
        $injector = new Injector();
908
        $config = array(
909
            'TestService' => array(
910
                'class' => TestObject::class,
911
                'calls' => array(
912
                    array('method', array('args'), 'what is this?')
913
                )
914
            )
915
        );
916
917
        $injector->load($config);
918
        $item = $injector->get('TestService');
919
    }
920
921
922
923
    /**
924
     * Test nesting of injector
925
     */
926
    public function testNest()
927
    {
928
929
        // Outer nest to avoid interference with other
930
        Injector::nest();
931
        $this->nestingLevel++;
932
933
        // Test services
934
        $config = array(
935
            NewRequirementsBackend::class,
936
        );
937
        Injector::inst()->load($config);
938
        $si = Injector::inst()->get(TestStaticInjections::class);
939
        $this->assertInstanceOf(TestStaticInjections::class, $si);
940
        $this->assertInstanceOf(NewRequirementsBackend::class, $si->backend);
941
        $this->assertInstanceOf(MyParentClass::class, Injector::inst()->get(MyParentClass::class));
942
        $this->assertInstanceOf(MyChildClass::class, Injector::inst()->get(MyChildClass::class));
943
944
        // Test that nested injector values can be overridden
945
        Injector::nest();
946
        $this->nestingLevel++;
947
        Injector::inst()->unregisterAllObjects();
948
        $newsi = Injector::inst()->get(TestStaticInjections::class);
949
        $newsi->backend = new InjectorTest\OriginalRequirementsBackend();
950
        Injector::inst()->registerService($newsi, TestStaticInjections::class);
951
        Injector::inst()->registerService(new InjectorTest\MyChildClass(), MyParentClass::class);
952
953
        // Check that these overridden values are retrievable
954
        $si = Injector::inst()->get(TestStaticInjections::class);
955
        $this->assertInstanceOf(TestStaticInjections::class, $si);
956
        $this->assertInstanceOf(OriginalRequirementsBackend::class, $si->backend);
957
        $this->assertInstanceOf(MyParentClass::class, Injector::inst()->get(MyParentClass::class));
958
        $this->assertInstanceOf(MyParentClass::class, Injector::inst()->get(MyChildClass::class));
959
960
        // Test that unnesting restores expected behaviour
961
        Injector::unnest();
962
        $this->nestingLevel--;
963
        $si = Injector::inst()->get(TestStaticInjections::class);
964
        $this->assertInstanceOf(TestStaticInjections::class, $si);
965
        $this->assertInstanceOf(NewRequirementsBackend::class, $si->backend);
966
        $this->assertInstanceOf(MyParentClass::class, Injector::inst()->get(MyParentClass::class));
967
        $this->assertInstanceOf(MyChildClass::class, Injector::inst()->get(MyChildClass::class));
968
969
        // Test reset of cache
970
        Injector::inst()->unregisterAllObjects();
971
        $si = Injector::inst()->get(TestStaticInjections::class);
972
        $this->assertInstanceOf(TestStaticInjections::class, $si);
973
        $this->assertInstanceOf(NewRequirementsBackend::class, $si->backend);
974
        $this->assertInstanceOf(MyParentClass::class, Injector::inst()->get(MyParentClass::class));
975
        $this->assertInstanceOf(MyChildClass::class, Injector::inst()->get(MyChildClass::class));
976
977
        // Return to nestingLevel 0
978
        Injector::unnest();
979
        $this->nestingLevel--;
980
    }
981
}
982