├── .github
├── FUNDING.yml
└── workflows
│ ├── continuous-integration.yml
│ └── release-on-milestone-closed.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── build
└── .gitignore
├── composer.json
├── composer.lock
├── examples
└── simple-instantiator-map.php
├── infection.json.dist
├── phpbench.json
├── phpcs.xml.dist
├── phpunit.xml.dist
├── psalm.xml
├── renovate.json
├── src
└── LazyMap
│ ├── AbstractLazyMap.php
│ └── CallbackLazyMap.php
└── test
├── LazyMapPerformance
└── LazyMapBench.php
├── LazyMapTest
├── AbstractLazyMapTest.php
├── CallbackLazyMapTest.php
└── NullLazyMapTest.php
└── LazyMapTestAsset
├── CallableClass.php
├── NullArrayBasedLazyMap.php
└── NullLazyMap.php
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [Ocramius]
2 |
--------------------------------------------------------------------------------
/.github/workflows/continuous-integration.yml:
--------------------------------------------------------------------------------
1 | # See https://github.com/laminas/laminas-continuous-integration-action
2 | # Generates a job matrix based on current dependencies and supported version
3 | # ranges, then runs all those jobs
4 | name: "Continuous Integration"
5 |
6 | on:
7 | pull_request:
8 | push:
9 |
10 | jobs:
11 | matrix:
12 | name: Generate job matrix
13 | runs-on: ubuntu-latest
14 | outputs:
15 | matrix: ${{ steps.matrix.outputs.matrix }}
16 | steps:
17 | - name: Gather CI configuration
18 | id: matrix
19 | uses: laminas/laminas-ci-matrix-action@1.22.1
20 |
21 | qa:
22 | name: QA Checks
23 | needs: [ matrix ]
24 | runs-on: ${{ matrix.operatingSystem }}
25 | strategy:
26 | fail-fast: false
27 | matrix: ${{ fromJSON(needs.matrix.outputs.matrix) }}
28 | steps:
29 | - name: ${{ matrix.name }}
30 | uses: laminas/laminas-continuous-integration-action@1.32.0
31 | env:
32 | "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
33 | "INFECTION_DASHBOARD_API_KEY": ${{ secrets.INFECTION_DASHBOARD_API_KEY }}
34 | "STRYKER_DASHBOARD_API_KEY": ${{ secrets.STRYKER_DASHBOARD_API_KEY }}
35 | with:
36 | job: ${{ matrix.job }}
37 |
38 | demo-scripts:
39 | name: "Check Demo Scripts"
40 |
41 | runs-on: ${{ matrix.operating-system }}
42 |
43 | strategy:
44 | matrix:
45 | dependencies:
46 | - "locked"
47 | php-version:
48 | - "8.1"
49 | - "8.2"
50 | operating-system:
51 | - "ubuntu-latest"
52 |
53 | steps:
54 | - name: "Checkout"
55 | uses: "actions/checkout@v3"
56 |
57 | - name: "Install PHP"
58 | uses: "shivammathur/setup-php@2.24.0"
59 | with:
60 | coverage: "pcov"
61 | php-version: "${{ matrix.php-version }}"
62 | tools: composer:v2, cs2pr
63 |
64 | - name: "Cache dependencies"
65 | uses: "actions/cache@v3"
66 | with:
67 | path: |
68 | ~/.composer/cache
69 | vendor
70 | key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}"
71 | restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}"
72 |
73 | - name: "Install lowest dependencies"
74 | if: ${{ matrix.dependencies == 'lowest' }}
75 | run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest"
76 |
77 | - name: "Install highest dependencies"
78 | if: ${{ matrix.dependencies == 'highest' }}
79 | run: "composer update --no-interaction --no-progress --no-suggest"
80 |
81 | - name: "Install locked dependencies"
82 | if: ${{ matrix.dependencies == 'locked' }}
83 | run: "composer install --no-interaction --no-progress --no-suggest"
84 |
85 | - name: "Check Demo Scripts"
86 | run: |
87 | php examples/simple-instantiator-map.php
88 |
--------------------------------------------------------------------------------
/.github/workflows/release-on-milestone-closed.yml:
--------------------------------------------------------------------------------
1 | # https://help.github.com/en/categories/automating-your-workflow-with-github-actions
2 |
3 | name: "Automatic Releases"
4 |
5 | on:
6 | milestone:
7 | types:
8 | - "closed"
9 |
10 | jobs:
11 | release:
12 | name: "GIT tag, release & create merge-up PR"
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - name: "Checkout"
17 | uses: "actions/checkout@v3"
18 |
19 | - name: "Release"
20 | uses: "laminas/automatic-releases@v1"
21 | with:
22 | command-name: "laminas:automatic-releases:release"
23 | env:
24 | "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
25 | "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
26 | "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
27 | "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
28 |
29 | - name: "Create Merge-Up Pull Request"
30 | uses: "laminas/automatic-releases@v1"
31 | with:
32 | command-name: "laminas:automatic-releases:create-merge-up-pull-request"
33 | env:
34 | "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
35 | "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
36 | "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
37 | "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
38 |
39 | - name: "Create and/or Switch to new Release Branch"
40 | uses: "laminas/automatic-releases@v1"
41 | with:
42 | command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor"
43 | env:
44 | "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}
45 | "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
46 | "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
47 | "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
48 |
49 | - name: "Bump Changelog Version On Originating Release Branch"
50 | uses: "laminas/automatic-releases@v1"
51 | with:
52 | command-name: "laminas:automatic-releases:bump-changelog"
53 | env:
54 | "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
55 | "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
56 | "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
57 | "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
58 |
59 | - name: "Create new milestones"
60 | uses: "laminas/automatic-releases@v1"
61 | with:
62 | command-name: "laminas:automatic-releases:create-milestones"
63 | env:
64 | "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
65 | "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
66 | "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
67 | "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | infection.log
3 | phpunit.xml
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | * Coding standard for the project is [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
4 | * The project will follow strict [object calisthenics](http://www.slideshare.net/guilhermeblanco/object-calisthenics-applied-to-php)
5 | * Any contribution must provide tests for additional introduced conditions
6 | * Any un-confirmed issue needs a failing test case before being accepted
7 | * Pull requests must be sent from a new hotfix/feature branch, not from `master`.
8 |
9 | ## Installation
10 |
11 | To install the project and run the tests, you need to clone it first:
12 |
13 | ```sh
14 | $ git clone git://github.com/Ocramius/LazyMap.git
15 | ```
16 |
17 | You will then need to run a composer installation:
18 |
19 | ```sh
20 | $ cd LazyMap
21 | $ curl -s https://getcomposer.org/installer | php
22 | $ php composer.phar update
23 | ```
24 |
25 | ## Testing
26 |
27 | The PHPUnit version to be used is the one installed as a dev- dependency via composer:
28 |
29 | ```sh
30 | $ ./vendor/bin/phpunit
31 | ```
32 |
33 | Accepted coverage for new contributions is 80%. Any contribution not satisfying this requirement
34 | won't be merged.
35 |
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Marco Pivetta
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lazy Map
2 |
3 | This small library aims at providing a very simple and efficient map of lazy-instantiating objects.
4 |
5 | [](https://packagist.org/packages/ocramius/lazy-map)
6 | [](https://packagist.org/packages/ocramius/lazy-map)
7 | [](https://packagist.org/packages/ocramius/lazy-map)
8 | [](https://dashboard.stryker-mutator.io/reports/github.com/Ocramius/LazyMap/2.5.x)
9 |
10 | ## Abandoned
11 |
12 | Starting with PHP 8.3, dynamic properties are [no longer allowed "out of the box"](https://wiki.php.net/rfc/deprecate_dynamic_properties).
13 | While it is still possible to have dynamic properties via explicit declaration (the `#[\AllowDynamicProperties]` attribute), the approach
14 | of this package is no longer to be considered safe nor efficient long-term.
15 |
16 | Based on that, this package is deprecated and abandoned: please use traditional PHP `array`s instead.
17 |
18 | ## Installation
19 |
20 | The suggested installation method is via [composer](https://getcomposer.org/):
21 |
22 | ```sh
23 | composer require ocramius/lazy-map
24 | ```
25 |
26 | ## Usage
27 |
28 | The current implementation is very simple and allows to define a map of "services" through a
29 | `LazyMap\CallbackLazyMap`:
30 |
31 | ```php
32 | $map = new \LazyMap\CallbackLazyMap(function ($name) {
33 | $object = new \stdClass();
34 |
35 | $object->name = $name;
36 |
37 | return $object;
38 | });
39 |
40 | var_dump($map->foo);
41 | var_dump($map->bar);
42 | var_dump($map->{'something special'});
43 | ```
44 |
45 | ## Purpose
46 |
47 | The idea behind the library is to avoid un-efficient lazy-loading operations like following:
48 |
49 | ```php
50 | private function getSomething($name)
51 | {
52 | if (isset($this->initialized[$name]) || array_key_exists($name, $this->initialized)) {
53 | return $this->initialized[$name];
54 | }
55 |
56 | return $this->initialized[$name] = new Something($name);
57 | }
58 | ```
59 |
60 | This reduces overhead greatly when you'd otherwise call `getSomething()` thousands of times.
61 | That's especially useful when mapping a lot of different services and iterating over them
62 | over and over again.
63 |
64 | ## Performance
65 |
66 | LazyMap actually performs much better than the "un-efficient" example that I've shown above.
67 | You can look directly at the performance test suite for details on the tested implementations,
68 | but here are some results for you to have an idea of the boost:
69 |
70 | #### Initialized Map Performance:
71 |
72 | |Method Name |Ops/s |Relative|
73 | |--------------------------------|---------------|--------|
74 | |initializedArrayPerformance |2,277,272.90002|100.00% |
75 | |initializedArrayMapPerformance |1,536,988.76108|148.16% |
76 | |initializedLazyMapPerformance |4,446,227.23514|51.22% |
77 |
78 |
79 | #### Un-Initialized Map Performance:
80 |
81 | |Method Name |Ops/s |Relative|
82 | |--------------------------------|---------------|--------|
83 | |unInitializedArrayPerformance : |1,091,720.80627|100.00% |
84 | |unInitializedArrayMapPerformance|688,132.30083 |158.65% |
85 | |unInitializedLazyMapPerformance:|912,191.90744 |119.68% |
86 |
--------------------------------------------------------------------------------
/build/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !logs
3 | !.gitignore
4 | !coverage-checker.php
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ocramius/lazy-map",
3 | "description": "A library that provides lazy instantiation logic for a map of objects",
4 | "type": "library",
5 | "license": "MIT",
6 | "homepage": "https://github.com/Ocramius/LazyMap",
7 | "keywords": [
8 | "lazy",
9 | "lazy loading",
10 | "lazy instantiation",
11 | "map",
12 | "service location"
13 | ],
14 | "authors": [
15 | {
16 | "name": "Marco Pivetta",
17 | "email": "ocramius@gmail.com",
18 | "homepage": "http://ocramius.github.io/"
19 | }
20 | ],
21 | "require": {
22 | "php": "~8.1.0 || ~8.2.0"
23 | },
24 | "require-dev": {
25 | "doctrine/coding-standard": "^11.1.0",
26 | "roave/infection-static-analysis-plugin": "^1.29.0",
27 | "phpbench/phpbench": "^1.2.9",
28 | "phpunit/phpunit": "^9.6.5",
29 | "vimeo/psalm": "^5.7.7"
30 | },
31 | "autoload": {
32 | "psr-4": {
33 | "LazyMap\\": "src/LazyMap"
34 | }
35 | },
36 | "autoload-dev": {
37 | "psr-4": {
38 | "LazyMapPerformance\\": "test/LazyMapPerformance",
39 | "LazyMapTest\\": "test/LazyMapTest",
40 | "LazyMapTestAsset\\": "test/LazyMapTestAsset"
41 | }
42 | },
43 | "config": {
44 | "allow-plugins": {
45 | "dealerdirect/phpcodesniffer-composer-installer": true,
46 | "infection/extension-installer": false
47 | },
48 | "platform": {
49 | "php": "8.1.99"
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/examples/simple-instantiator-map.php:
--------------------------------------------------------------------------------
1 | name = $name;
13 |
14 | return $object;
15 | });
16 |
17 | // the map instantiates objects lazily
18 | var_dump($map->foo);
19 | var_dump($map->bar);
20 | var_dump($map->baz);
21 |
22 | // same properties return the same object (shared instance)
23 | var_dump($map->foo === $map->foo);
24 |
25 | // different properties contain different instances
26 | var_dump($map->foo === $map->bar);
27 | var_dump($map->bar === $map->baz);
28 | var_dump($map->baz === $map->foo);
29 |
--------------------------------------------------------------------------------
/infection.json.dist:
--------------------------------------------------------------------------------
1 | {
2 | "source": {
3 | "directories": [
4 | "src"
5 | ]
6 | },
7 | "logs": {
8 | "text": "php://stderr",
9 | "github": true,
10 | "stryker": {
11 | "report": "/^\\d+\\.\\d+\\.x$/"
12 | }
13 | },
14 | "minMsi": 100,
15 | "minCoveredMsi": 100
16 | }
17 |
--------------------------------------------------------------------------------
/phpbench.json:
--------------------------------------------------------------------------------
1 | {
2 | "runner.bootstrap": "vendor/autoload.php",
3 | "runner.path": "test/LazyMapPerformance",
4 | "runner.retry_threshold": 5,
5 | "runner.warmup": 2,
6 | "report.outputs": {
7 | "report": {
8 | "extends": "aggregate"
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/phpcs.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | examples
17 | src
18 | test
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 | ./test/LazyMapTest
11 |
12 |
13 |
14 | ./src
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "local>Ocramius/.github:renovate-config"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/src/LazyMap/AbstractLazyMap.php:
--------------------------------------------------------------------------------
1 | $name = $this->instantiate($name);
24 |
25 | /** @psalm-suppress MixedReturnStatement */
26 | return $this->$name;
27 | }
28 |
29 | /**
30 | * Instantiate a particular key by the given name
31 | *
32 | * @return mixed
33 | * @psalm-return T
34 | */
35 | abstract protected function instantiate(string $name);
36 | }
37 |
--------------------------------------------------------------------------------
/src/LazyMap/CallbackLazyMap.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | #[AllowDynamicProperties]
14 | final class CallbackLazyMap extends AbstractLazyMap
15 | {
16 | /** @psalm-param callable(string) : T $callback */
17 | public function __construct(callable $callback)
18 | {
19 | $this->{self::class . "\0callback"} = $callback;
20 | }
21 |
22 | /**
23 | * {@inheritDoc}
24 | */
25 | protected function instantiate(string $name)
26 | {
27 | /** @psalm-var callable(string) : T $callback */
28 | $callback = $this->{self::class . "\0callback"};
29 |
30 | return $callback($name);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/test/LazyMapPerformance/LazyMapBench.php:
--------------------------------------------------------------------------------
1 | */
17 | private array $array;
18 |
19 | private NullArrayBasedLazyMap $arrayMap;
20 |
21 | private NullLazyMap $lazyMap;
22 |
23 | public function setUp(): void
24 | {
25 | $this->array = ['existingKey' => 0];
26 | $this->arrayMap = new NullArrayBasedLazyMap();
27 | $this->lazyMap = new NullLazyMap();
28 |
29 | // enforcing key initialization
30 | $this->arrayMap->get('existingKey');
31 | $this->lazyMap->existingKey;
32 | }
33 |
34 | public function benchInitializedArrayPerformance(): int
35 | {
36 | if (array_key_exists('existingKey', $this->array)) {
37 | return $this->array['existingKey'];
38 | }
39 |
40 | return 0;
41 | }
42 |
43 | public function benchInitializedArrayMapPerformance(): int
44 | {
45 | return $this->arrayMap->get('existingKey');
46 | }
47 |
48 | /** @return null */
49 | public function benchInitializedLazyMapPerformance()
50 | {
51 | return $this->lazyMap->existingKey;
52 | }
53 |
54 | public function benchUnInitializedArrayPerformance(): int
55 | {
56 | if (array_key_exists('nonExistingKey', $this->array)) {
57 | return $this->array['nonExistingKey'];
58 | }
59 |
60 | return $this->array['nonExistingKey'] = 0;
61 | }
62 |
63 | public function benchUnInitializedArrayMapPerformance(): int
64 | {
65 | return $this->arrayMap->get('nonExistingKey');
66 | }
67 |
68 | /** @return null */
69 | public function benchUnInitializedLazyMapPerformance()
70 | {
71 | return $this->lazyMap->nonExistingKey;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/test/LazyMapTest/AbstractLazyMapTest.php:
--------------------------------------------------------------------------------
1 | lazyMap = $this->getMockForAbstractClass(AbstractLazyMap::class);
21 | }
22 |
23 | public function testDirectPropertyAccess(): void
24 | {
25 | /** @psalm-var AbstractLazyMap&MockObject $lazyMap */
26 | $lazyMap = $this->lazyMap;
27 |
28 | $lazyMap
29 | ->expects($this->exactly(3))
30 | ->method('instantiate')
31 | ->with($this->isType('string'))
32 | ->willReturnCallback(static function (string $key): string {
33 | return $key . ' - initialized value';
34 | });
35 |
36 | self::assertSame('foo - initialized value', $lazyMap->foo);
37 | self::assertSame('bar - initialized value', $lazyMap->bar);
38 | self::assertSame('baz\\tab - initialized value', $lazyMap->{'baz\\tab'});
39 | }
40 |
41 | public function testMultipleDirectPropertyAccessDoesNotTriggerSameInstantiation(): void
42 | {
43 | /** @psalm-var AbstractLazyMap&MockObject $lazyMap */
44 | $lazyMap = $this->lazyMap;
45 |
46 | $lazyMap
47 | ->expects($this->exactly(2))
48 | ->method('instantiate')
49 | ->with($this->isType('string'))
50 | ->willReturnCallback(static function (): stdClass {
51 | return new stdClass();
52 | });
53 |
54 | $foo = $lazyMap->foo;
55 |
56 | self::assertSame($foo, $lazyMap->foo);
57 |
58 | $bar = $lazyMap->bar;
59 |
60 | self::assertSame($bar, $lazyMap->bar);
61 |
62 | self::assertNotSame($bar, $foo);
63 | }
64 |
65 | public function testUnSettingPropertiesRemovesSharedInstance(): void
66 | {
67 | /** @psalm-var AbstractLazyMap&MockObject $lazyMap */
68 | $lazyMap = $this->lazyMap;
69 |
70 | $lazyMap
71 | ->expects($this->exactly(2))
72 | ->method('instantiate')
73 | ->with($this->isType('string'))
74 | ->willReturnCallback(static function (): stdClass {
75 | return new stdClass();
76 | });
77 |
78 | $foo = $lazyMap->foo;
79 |
80 | self::assertSame($foo, $lazyMap->foo);
81 |
82 | unset($lazyMap->foo);
83 |
84 | $bar = $lazyMap->foo;
85 |
86 | self::assertSame($bar, $lazyMap->foo);
87 | self::assertNotSame($bar, $foo);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/test/LazyMapTest/CallbackLazyMapTest.php:
--------------------------------------------------------------------------------
1 | callback = $this->createMock(CallableClass::class);
23 | $this->lazyMap = new CallbackLazyMap(function (string $name): string {
24 | return $this->callback->__invoke($name);
25 | });
26 | }
27 |
28 | public function testDirectPropertyAccess(): void
29 | {
30 | $count = 0;
31 | $this
32 | ->callback
33 | ->expects($this->exactly(3))
34 | ->method('__invoke')
35 | ->willReturnCallback(static function (string $name) use (& $count): string {
36 | self::assertIsInt($count);
37 |
38 | $count += 1;
39 |
40 | return $name . ' - ' . $count;
41 | });
42 |
43 | self::assertEquals('foo - 1', $this->lazyMap->foo);
44 | self::assertEquals('bar - 2', $this->lazyMap->bar);
45 | self::assertEquals('baz\\tab - 3', $this->lazyMap->{'baz\\tab'});
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/test/LazyMapTest/NullLazyMapTest.php:
--------------------------------------------------------------------------------
1 | lazyMap = new NullLazyMap();
19 | }
20 |
21 | public function testDirectPropertyAccess(): void
22 | {
23 | self::assertSame(null, $this->lazyMap->foo);
24 | self::assertSame(null, $this->lazyMap->bar);
25 | self::assertSame(null, $this->lazyMap->{'baz\\tab'});
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/LazyMapTestAsset/CallableClass.php:
--------------------------------------------------------------------------------
1 | */
15 | private array $items = [];
16 |
17 | /** Lazy getter - retrieves or instantiates a key in the map */
18 | public function & get(string $name): int
19 | {
20 | if (isset($this->items[$name]) || array_key_exists($name, $this->items)) {
21 | return $this->items[$name];
22 | }
23 |
24 | $this->items[$name] = $this->instantiate($name);
25 |
26 | return $this->items[$name];
27 | }
28 |
29 | /**
30 | * Null instantiator, emulates same overhead of an {@see \LazyMapTestAsset\NullLazyMap}
31 | */
32 | protected function instantiate(string $name): int
33 | {
34 | return 0;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/test/LazyMapTestAsset/NullLazyMap.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | #[AllowDynamicProperties]
16 | class NullLazyMap extends AbstractLazyMap
17 | {
18 | /** @return null */
19 | protected function instantiate(string $name)
20 | {
21 | return null;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------