PK'ZwDU map.rst.incnuW+A* :doc:`/mockery/configuration` * :doc:`/mockery/exceptions` * :doc:`/mockery/reserved_method_names` * :doc:`/mockery/gotchas` PK'Z :e index.rstnuW+AMockery ======= .. toctree:: :hidden: configuration exceptions reserved_method_names gotchas .. include:: map.rst.inc PK'Z_o o exceptions.rstnuW+A.. index:: single: Mockery; Exceptions Mockery Exceptions ================== Mockery throws three types of exceptions when it cannot verify a mock object. #. ``\Mockery\Exception\InvalidCountException`` #. ``\Mockery\Exception\InvalidOrderException`` #. ``\Mockery\Exception\NoMatchingExpectationException`` You can capture any of these exceptions in a try...catch block to query them for specific information which is also passed along in the exception message but is provided separately from getters should they be useful when logging or reformatting output. \Mockery\Exception\InvalidCountException ---------------------------------------- The exception class is used when a method is called too many (or too few) times and offers the following methods: * ``getMock()`` - return actual mock object * ``getMockName()`` - return the name of the mock object * ``getMethodName()`` - return the name of the method the failing expectation is attached to * ``getExpectedCount()`` - return expected calls * ``getExpectedCountComparative()`` - returns a string, e.g. ``<=`` used to compare to actual count * ``getActualCount()`` - return actual calls made with given argument constraints \Mockery\Exception\InvalidOrderException ---------------------------------------- The exception class is used when a method is called outside the expected order set using the ``ordered()`` and ``globally()`` expectation modifiers. It offers the following methods: * ``getMock()`` - return actual mock object * ``getMockName()`` - return the name of the mock object * ``getMethodName()`` - return the name of the method the failing expectation is attached to * ``getExpectedOrder()`` - returns an integer represented the expected index for which this call was expected * ``getActualOrder()`` - return the actual index at which this method call occurred. \Mockery\Exception\NoMatchingExpectationException ------------------------------------------------- The exception class is used when a method call does not match any known expectation. All expectations are uniquely identified in a mock object by the method name and the list of expected arguments. You can disable this exception and opt for returning NULL from all unexpected method calls by using the earlier mentioned shouldIgnoreMissing() behaviour modifier. This exception class offers the following methods: * ``getMock()`` - return actual mock object * ``getMockName()`` - return the name of the mock object * ``getMethodName()`` - return the name of the method the failing expectation is attached to * ``getActualArguments()`` - return actual arguments used to search for a matching expectation PK'Z#xUUreserved_method_names.rstnuW+A.. index:: single: Reserved Method Names Reserved Method Names ===================== As you may have noticed, Mockery uses a number of methods called directly on all mock objects, for example ``shouldReceive()``. Such methods are necessary in order to setup expectations on the given mock, and so they cannot be implemented on the classes or objects being mocked without creating a method name collision (reported as a PHP fatal error). The methods reserved by Mockery are: * ``shouldReceive()`` * ``shouldNotReceive()`` * ``allows()`` * ``expects()`` * ``shouldAllowMockingMethod()`` * ``shouldIgnoreMissing()`` * ``asUndefined()`` * ``shouldAllowMockingProtectedMethods()`` * ``makePartial()`` * ``byDefault()`` * ``shouldHaveReceived()`` * ``shouldHaveBeenCalled()`` * ``shouldNotHaveReceived()`` * ``shouldNotHaveBeenCalled()`` In addition, all mocks utilise a set of added methods and protected properties which cannot exist on the class or object being mocked. These are far less likely to cause collisions. All properties are prefixed with ``_mockery`` and all method names with ``mockery_``. PK'Z&#yconfiguration.rstnuW+A.. index:: single: Mockery; Configuration Mockery Global Configuration ============================ To allow for a degree of fine-tuning, Mockery utilises a singleton configuration object to store a small subset of core behaviours. The three currently present include: * Option to allow/disallow the mocking of methods which do not actually exist fulfilled (i.e. unused) * Setter/Getter for added a parameter map for internal PHP class methods (``Reflection`` cannot detect these automatically) * Option to drive if quick definitions should define a stub or a mock with an 'at least once' expectation. By default, the first behaviour is enabled. Of course, there are situations where this can lead to unintended consequences. The mocking of non-existent methods may allow mocks based on real classes/objects to fall out of sync with the actual implementations, especially when some degree of integration testing (testing of object wiring) is not being performed. You may allow or disallow this behaviour (whether for whole test suites or just select tests) by using the following call: .. code-block:: php \Mockery::getConfiguration()->allowMockingNonExistentMethods(bool); Passing a true allows the behaviour, false disallows it. It takes effect immediately until switched back. If the behaviour is detected when not allowed, it will result in an Exception being thrown at that point. Note that disallowing this behaviour should be carefully considered since it necessarily removes at least some of Mockery's flexibility. The other two methods are: .. code-block:: php \Mockery::getConfiguration()->setInternalClassMethodParamMap($class, $method, array $paramMap) \Mockery::getConfiguration()->getInternalClassMethodParamMap($class, $method) These are used to define parameters (i.e. the signature string of each) for the methods of internal PHP classes (e.g. SPL, or PECL extension classes like ext/mongo's MongoCollection. Reflection cannot analyse the parameters of internal classes. Most of the time, you never need to do this. It's mainly needed where an internal class method uses pass-by-reference for a parameter - you MUST in such cases ensure the parameter signature includes the ``&`` symbol correctly as Mockery won't correctly add it automatically for internal classes. Note that internal class parameter overriding is not available in PHP 8. This is because incompatible signatures have been reclassified as fatal errors. Finally there is the possibility to change what a quick definition produces. By default quick definitions create stubs but you can change this behaviour by asking Mockery to use 'at least once' expectations. .. code-block:: php \Mockery::getConfiguration()->getQuickDefinitions()->shouldBeCalledAtLeastOnce(bool) Passing a true allows the behaviour, false disallows it. It takes effect immediately until switched back. By doing so you can avoid the proliferating of quick definitions that accumulate overtime in your code since the test would fail in case the 'at least once' expectation is not fulfilled. Disabling reflection caching ---------------------------- Mockery heavily uses `"reflection" `_ to do it's job. To speed up things, Mockery caches internally the information it gathers via reflection. In some cases, this caching can cause problems. The **only** known situation when this occurs is when PHPUnit's ``--static-backup`` option is used. If you use ``--static-backup`` and you get an error that looks like the following: .. code-block:: php Error: Internal error: Failed to retrieve the reflection object We suggest turning off the reflection cache as so: .. code-block:: php \Mockery::getConfiguration()->disableReflectionCache(); Turning it back on can be done like so: .. code-block:: php \Mockery::getConfiguration()->enableReflectionCache(); In no other situation should you be required turn this reflection cache off. PK'ZY  gotchas.rstnuW+A.. index:: single: Mockery; Gotchas Gotchas! ======== Mocking objects in PHP has its limitations and gotchas. Some functionality can't be mocked or can't be mocked YET! If you locate such a circumstance, please please (pretty please with sugar on top) create a new issue on GitHub so it can be documented and resolved where possible. Here is a list to note: 1. Classes containing public ``__wakeup()`` methods can be mocked but the mocked ``__wakeup()`` method will perform no actions and cannot have expectations set for it. This is necessary since Mockery must serialize and unserialize objects to avoid some ``__construct()`` insanity and attempting to mock a ``__wakeup()`` method as normal leads to a ``BadMethodCallException`` being thrown. 2. Mockery has two scenarios where real classes are replaced: Instance mocks and alias mocks. Both will generate PHP fatal errors if the real class is loaded, usually via a require or include statement. Only use these two mock types where autoloading is in place and where classes are not explicitly loaded on a per-file basis using ``require()``, ``require_once()``, etc. 3. Internal PHP classes are not entirely capable of being fully analysed using ``Reflection``. For example, ``Reflection`` cannot reveal details of expected parameters to the methods of such internal classes. As a result, there will be problems where a method parameter is defined to accept a value by reference (Mockery cannot detect this condition and will assume a pass by value on scalars and arrays). If references as internal class method parameters are needed, you should use the ``\Mockery\Configuration::setInternalClassMethodParamMap()`` method. Note, however that internal class parameter overriding is not available in PHP 8 since incompatible signatures have been reclassified as fatal errors. 4. Creating a mock implementing a certain interface with incorrect case in the interface name, and then creating a second mock implementing the same interface, but this time with the correct case, will have undefined behavior due to PHP's ``class_exists`` and related functions being case insensitive. Using the ``::class`` keyword in PHP can help you avoid these mistakes. The gotchas noted above are largely down to PHP's architecture and are assumed to be unavoidable. But - if you figure out a solution (or a better one than what may exist), let us know! PKZrrmockery/.readthedocs.ymlnuW+A# Read the Docs configuration file for Sphinx projects # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the OS, Python version and other tools we might need build: os: ubuntu-22.04 tools: python: "3.12" # Build documentation in the "docs/" directory with Sphinx sphinx: configuration: docs/conf.py # Build documentation in additional formats such as PDF and ePub formats: all # Build requirements for our documentation # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - requirements: docs/requirements.txt PKZس mockery/CONTRIBUTING.mdnuW+A# Contributing We'd love you to help out with mockery and no contribution is too small. ## Reporting Bugs Issues can be reported on the [issue tracker](https://github.com/mockery/mockery/issues). Please try and report any bugs with a minimal reproducible example, it will make things easier for other contributors and your problems will hopefully be resolved quickly. ## Requesting Features We're always interested to hear about your ideas and you can request features by creating a ticket in the [issue tracker](https://github.com/mockery/mockery/issues). We can't always guarantee someone will jump on it straight away, but putting it out there to see if anyone else is interested is a good idea. Likewise, if a feature you would like is already listed in the issue tracker, add a :+1: so that other contributors know it's a feature that would help others. ## Contributing code and documentation We loosely follow the [PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md) and [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) coding standards, but we'll probably merge any code that looks close enough. * Fork the [repository](https://github.com/mockery/mockery) on GitHub * Add the code for your feature or bug * Add some tests for your feature or bug * Optionally, but preferably, write some documentation * Optionally, update the CHANGELOG.md file with your feature or [BC](http://en.wikipedia.org/wiki/Backward_compatibility) break * Send a [Pull Request](https://help.github.com/articles/creating-a-pull-request) to the correct target branch (see below) If you have a big change or would like to discuss something, create an issue in the [issue tracker](https://github.com/mockery/mockery/issues) or jump in to \#mockery on freenode Any code you contribute must be licensed under the [BSD 3-Clause License](http://opensource.org/licenses/BSD-3-Clause). ## Target Branch Mockery may have several active branches at any one time and roughly follows a [Git Branching Model](https://igor.io/2013/10/21/git-branching-model.html). Generally, if you're developing a new feature, you want to be targeting the master branch, if it's a bug fix, you want to be targeting a release branch, e.g. 0.8. ## Testing Mockery To run the unit tests for Mockery, clone the git repository, download Composer using the instructions at [http://getcomposer.org/download/](http://getcomposer.org/download/), then install the dependencies with `php /path/to/composer.phar install`. This will install the required dev dependencies and create the autoload files required by the unit tests. You may run the `vendor/bin/phpunit` command to run the unit tests. If everything goes to plan, there will be no failed tests! ## Debugging Mockery Mockery and its code generation can be difficult to debug. A good start is to use the `RequireLoader`, which will dump the code generated by mockery to a file before requiring it, rather than using eval. This will help with stack traces, and you will be able to open the mock class in your editor. ``` php // tests/bootstrap.php Mockery::setLoader(new Mockery\Loader\RequireLoader(sys_get_temp_dir())); ``` PKZ}mockery/LICENSEnuW+ABSD 3-Clause License Copyright (c) 2009-2023, Pádraic Brady All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKZqbYUUmockery/CHANGELOG.mdnuW+A# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [1.6.12] - 2024-05-15 ### Changed - [1420: Update `psalm-baseline.xml` ](https://github.com/mockery/mockery/pull/1420) - [1419: Update e2e-test.sh](https://github.com/mockery/mockery/pull/1419) - [1413: Upgrade `phar` tools and `phive.xml` configuration](https://github.com/mockery/mockery/pull/1413) ### Fixed - [1415: Fix mocking anonymous classes](https://github.com/mockery/mockery/pull/1415) - [1411: Mocking final classes reports unresolvable type by PHPStan](https://github.com/mockery/mockery/issues/1411) - [1410: Fix PHP Doc Comments](https://github.com/mockery/mockery/pull/1410) ### Security - [1417: Bump `Jinja2` from `3.1.3` to `3.1.4` fix CVE-2024-34064](https://github.com/mockery/mockery/pull/1417) - [1412: Bump `idna` from `3.6` to `3.7` fix CVE-2024-3651](https://github.com/mockery/mockery/pull/1412) ## [1.6.11] - 2024-03-21 ### Fixed - [1407: Fix constants map generics doc comments](https://github.com/mockery/mockery/pull/1407) - [1406: Fix reserved words used to name a class, interface or trait](https://github.com/mockery/mockery/pull/1406) - [1403: Fix regression - partial construction with trait methods](https://github.com/mockery/mockery/pull/1403) - [1401: Improve `Mockery::mock()` parameter type compatibility with array typehints](https://github.com/mockery/mockery/pull/1401) ## [1.6.10] - 2024-03-19 ### Added - [1398: [PHP 8.4] Fixes for implicit nullability deprecation](https://github.com/mockery/mockery/pull/1398) ### Fixed - [1397: Fix mock method $args parameter type](https://github.com/mockery/mockery/pull/1397) - [1396: Fix `1.6.8` release](https://github.com/mockery/mockery/pull/1396) ## [1.6.9] - 2024-03-12 - [1394: Revert v1.6.8 release](https://github.com/mockery/mockery/pull/1394) ## [1.6.8] - 2024-03-12 - [1393: Changelog v1.6.8](https://github.com/mockery/mockery/pull/1393) - [1392: Refactor remaining codebase](https://github.com/mockery/mockery/pull/1392) - [1391: Update actions to use Node 20](https://github.com/mockery/mockery/pull/1391) - [1390: Update `ReadTheDocs` dependencies](https://github.com/mockery/mockery/pull/1390) - [1389: Refactor `library/Mockery/Matcher/*`](https://github.com/mockery/mockery/pull/1389) - [1388: Refactor `library/Mockery/Loader/*`](https://github.com/mockery/mockery/pull/1388) - [1387: Refactor `library/Mockery/CountValidator/*`](https://github.com/mockery/mockery/pull/1387) - [1386: Add PHPUnit 10+ attributes](https://github.com/mockery/mockery/pull/1386) - [1385: Update composer dependencies and clean up](https://github.com/mockery/mockery/pull/1385) - [1384: Update `psalm-baseline.xml`](https://github.com/mockery/mockery/pull/1384) - [1383: Refactor `library/helpers.php`](https://github.com/mockery/mockery/pull/1383) - [1382: Refactor `library/Mockery/VerificationExpectation.php`](https://github.com/mockery/mockery/pull/1382) - [1381: Refactor `library/Mockery/VerificationDirector.php`](https://github.com/mockery/mockery/pull/1381) - [1380: Refactor `library/Mockery/QuickDefinitionsConfiguration.php`](https://github.com/mockery/mockery/pull/1380) - [1379: Refactor `library/Mockery/Undefined.php`](https://github.com/mockery/mockery/pull/1379) - [1378: Refactor `library/Mockery/Reflector.php`](https://github.com/mockery/mockery/pull/1378) - [1377: Refactor `library/Mockery/ReceivedMethodCalls.php`](https://github.com/mockery/mockery/pull/1377) - [1376: Refactor `library/Mockery.php`](https://github.com/mockery/mockery/pull/1376) - [1375: Refactor `library/Mockery/MockInterface.php`](https://github.com/mockery/mockery/pull/1375) - [1374: Refactor `library/Mockery/MethodCall.php`](https://github.com/mockery/mockery/pull/1374) - [1373: Refactor `library/Mockery/LegacyMockInterface.php`](https://github.com/mockery/mockery/pull/1373) - [1372: Refactor `library/Mockery/Instantiator.php`](https://github.com/mockery/mockery/pull/1372) - [1371: Refactor `library/Mockery/HigherOrderMessage.php`](https://github.com/mockery/mockery/pull/1371) - [1370: Refactor `library/Mockery/ExpectsHigherOrderMessage.php`](https://github.com/mockery/mockery/pull/1370) - [1369: Refactor `library/Mockery/ExpectationInterface.php`](https://github.com/mockery/mockery/pull/1369) - [1368: Refactor `library/Mockery/ExpectationDirector.php`](https://github.com/mockery/mockery/pull/1368) - [1367: Refactor `library/Mockery/Expectation.php`](https://github.com/mockery/mockery/pull/1367) - [1366: Refactor `library/Mockery/Exception.php`](https://github.com/mockery/mockery/pull/1366) - [1365: Refactor `library/Mockery/Container.php`](https://github.com/mockery/mockery/pull/1365) - [1364: Refactor `library/Mockery/Configuration.php`](https://github.com/mockery/mockery/pull/1364) - [1363: Refactor `library/Mockery/CompositeExpectation.php`](https://github.com/mockery/mockery/pull/1363) - [1362: Refactor `library/Mockery/ClosureWrapper.php`](https://github.com/mockery/mockery/pull/1362) - [1361: Refactor `library/Mockery.php`](https://github.com/mockery/mockery/pull/1361) - [1360: Refactor Container](https://github.com/mockery/mockery/pull/1360) - [1355: Fix the namespace in the SubsetTest class](https://github.com/mockery/mockery/pull/1355) - [1354: Add array-like objects support to hasKey/hasValue matchers](https://github.com/mockery/mockery/pull/1354) ## [1.6.7] - 2023-12-09 ### Added - [#1338: Support PHPUnit constraints as matchers](https://github.com/mockery/mockery/pull/1338) - [#1336: Add factory methods for `IsEqual` and `IsSame` matchers](https://github.com/mockery/mockery/pull/1336) ### Fixed - [#1346: Fix test namespaces](https://github.com/mockery/mockery/pull/1346) - [#1343: Update documentation default theme and build version](https://github.com/mockery/mockery/pull/1343) - [#1329: Prevent `shouldNotReceive` from getting overridden by invocation count methods](https://github.com/mockery/mockery/pull/1329) ### Changed - [#1351: Update psalm-baseline.xml](https://github.com/mockery/mockery/pull/1351) - [#1350: Changelog v1.6.7](https://github.com/mockery/mockery/pull/1350) - [#1349: Cleanup](https://github.com/mockery/mockery/pull/1349) - [#1348: Update makefile](https://github.com/mockery/mockery/pull/1348) - [#1347: Bump phars dependencies](https://github.com/mockery/mockery/pull/1347) - [#1344: Disabled travis-ci and sensiolabs webhooks](https://github.com/mockery/mockery/issues/1344) - [#1342: Add `.readthedocs.yml` configuration](https://github.com/mockery/mockery/pull/1342) - [#1340: docs: Remove misplaced semicolumn from code snippet](https://github.com/mockery/mockery/pull/1340) ## 1.6.6 (2023-08-08) - [#1327: Changelog v1.6.6](https://github.com/mockery/mockery/pull/1327) - [#1325: Keep the file that caused an error for inspection](https://github.com/mockery/mockery/pull/1325) - [#1324: Fix Regression - Replace `+` Array Union Operator with `array_merge`](https://github.com/mockery/mockery/pull/1324) ## 1.6.5 (2023-08-05) - [#1322: Changelog v1.6.5](https://github.com/mockery/mockery/pull/1322) - [#1321: Autoload Test Fixtures Based on PHP Runtime Version](https://github.com/mockery/mockery/pull/1321) - [#1320: Clean up mocks on destruct](https://github.com/mockery/mockery/pull/1320) - [#1318: Fix misspelling in docs](https://github.com/mockery/mockery/pull/1318) - [#1316: Fix compatibility issues with PHP 7.3](https://github.com/mockery/mockery/pull/1316) - [#1315: Fix PHP 7.3 issues](https://github.com/mockery/mockery/issues/1315) - [#1314: Add Security Policy](https://github.com/mockery/mockery/pull/1314) - [#1313: Type declaration for `iterable|object`.](https://github.com/mockery/mockery/pull/1313) - [#1312: Mock disjunctive normal form types](https://github.com/mockery/mockery/pull/1312) - [#1299: Test PHP `8.3` language features](https://github.com/mockery/mockery/pull/1299) ## 1.6.4 (2023-07-19) - [#1308: Changelog v1.6.4](https://github.com/mockery/mockery/pull/1308) - [#1307: Revert `src` to `library` for `1.6.x`](https://github.com/mockery/mockery/pull/1307) ## 1.6.3 (2023-07-18) - [#1304: Remove `extra.branch-alias` and update composer information](https://github.com/mockery/mockery/pull/1304) - [#1303: Update `.gitattributes`](https://github.com/mockery/mockery/pull/1303) - [#1302: Changelog v1.6.3](https://github.com/mockery/mockery/pull/1302) - [#1301: Fix mocking classes with `new` initializers in method and attribute params on PHP 8.1](https://github.com/mockery/mockery/pull/1301) - [#1298: Update default repository branch to latest release branch](https://github.com/mockery/mockery/issues/1298) - [#1297: Update `Makefile` for contributors](https://github.com/mockery/mockery/pull/1297) - [#1294: Correct return types of Mock for phpstan](https://github.com/mockery/mockery/pull/1294) - [#1290: Rename directory `library` to `src`](https://github.com/mockery/mockery/pull/1290) - [#1288: Update codecov workflow](https://github.com/mockery/mockery/pull/1288) - [#1287: Update psalm configuration and workflow](https://github.com/mockery/mockery/pull/1287) - [#1286: Update phpunit workflow](https://github.com/mockery/mockery/pull/1286) - [#1285: Enforce the minimum required PHP version](https://github.com/mockery/mockery/pull/1285) - [#1283: Update license and copyright information](https://github.com/mockery/mockery/pull/1283) - [#1282: Create `COPYRIGHT.md` file](https://github.com/mockery/mockery/pull/1282) - [#1279: Bump `vimeo/psalm` from `5.9.0` to `5.12.0`](https://github.com/mockery/mockery/pull/1279) ## 1.6.2 (2023-06-07) - [#1276: Add `IsEqual` Argument Matcher](https://github.com/mockery/mockery/pull/1276) - [#1275: Add `IsSame` Argument Matcher](https://github.com/mockery/mockery/pull/1275) - [#1274: Update composer branch alias](https://github.com/mockery/mockery/pull/1274) - [#1271: Support PHP 8.2 `true` Literal Type](https://github.com/mockery/mockery/pull/1271) - [#1270: Support PHP 8.0 `false` Literal Type](https://github.com/mockery/mockery/pull/1270) ## 1.6.1 (2023-06-05) - [#1267 Drops support for PHP <7.4](https://github.com/mockery/mockery/pull/1267) - [#1192 Updated changelog for version 1.5.1 to include changes from #1180](https://github.com/mockery/mockery/pull/1192) - [#1196 Update example in README.md](https://github.com/mockery/mockery/pull/1196) - [#1199 Fix function parameter default enum value](https://github.com/mockery/mockery/pull/1199) - [#1205 Deal with null type in PHP8.2](https://github.com/mockery/mockery/pull/1205) - [#1208 Import MockeryTestCase fully qualified class name](https://github.com/mockery/mockery/pull/1208) - [#1210 Add support for target class attributes](https://github.com/mockery/mockery/pull/1210) - [#1212 docs: Add missing comma](https://github.com/mockery/mockery/pull/1212) - [#1216 Fixes code generation for intersection types](https://github.com/mockery/mockery/pull/1216) - [#1217 Add MockeryExceptionInterface](https://github.com/mockery/mockery/pull/1217) - [#1218 tidy: avoids require](https://github.com/mockery/mockery/pull/1218) - [#1222 Add .editorconfig](https://github.com/mockery/mockery/pull/1222) - [#1225 Switch to PSR-4 autoload](https://github.com/mockery/mockery/pull/1225) - [#1226 Refactoring risky tests](https://github.com/mockery/mockery/pull/1226) - [#1230 Add vimeo/psalm and psalm/plugin-phpunit](https://github.com/mockery/mockery/pull/1230) - [#1232 Split PHPUnit TestSuites for PHP 8.2](https://github.com/mockery/mockery/pull/1232) - [#1233 Bump actions/checkout to v3](https://github.com/mockery/mockery/pull/1233) - [#1234 Bump nick-invision/retry to v2](https://github.com/mockery/mockery/pull/1234) - [#1235 Setup Codecov for code coverage](https://github.com/mockery/mockery/pull/1235) - [#1236 Add Psalm CI Check](https://github.com/mockery/mockery/pull/1236) - [#1237 Unignore composer.lock file](https://github.com/mockery/mockery/pull/1237) - [#1239 Prevent CI run duplication](https://github.com/mockery/mockery/pull/1239) - [#1241 Add PHPUnit workflow for PHP 8.3](https://github.com/mockery/mockery/pull/1241) - [#1244 Improve ClassAttributesPass for Dynamic Properties](https://github.com/mockery/mockery/pull/1244) - [#1245 Deprecate hamcrest/hamcrest-php package](https://github.com/mockery/mockery/pull/1245) - [#1246 Add BUG_REPORT.yml Issue template](https://github.com/mockery/mockery/pull/1246) - [#1250 Deprecate PHP <=8.0](https://github.com/mockery/mockery/issues/1250) - [#1253 Prevent array to string conversion when serialising a Subset matcher](https://github.com/mockery/mockery/issues/1253) ## 1.6.0 (2023-06-05) [DELETED] This tag was deleted due to a mistake with the composer.json PHP version constraint, see [#1266](https://github.com/mockery/mockery/issues/1266) ## 1.3.6 (2022-09-07) - PHP 8.2 | Fix "Use of "parent" in callables is deprecated" notice #1169 ## 1.5.1 (2022-09-07) - [PHP 8.2] Various tests: explicitly declare properties #1170 - [PHP 8.2] Fix "Use of "parent" in callables is deprecated" notice #1169 - [PHP 8.1] Support intersection types #1164 - Handle final `__toString` methods #1162 - Only count assertions on expectations which can fail a test #1180 ## 1.5.0 (2022-01-20) - Override default call count expectations via expects() #1146 - Mock methods with static return types #1157 - Mock methods with mixed return type #1156 - Mock classes with new in initializers on PHP 8.1 #1160 - Removes redundant PHPUnitConstraint #1158 ## 1.4.4 (2021-09-13) - Fixes auto-generated return values #1144 - Adds support for tentative types #1130 - Fixes for PHP 8.1 Support (#1130 and #1140) - Add method that allows defining a set of arguments the mock should yield #1133 - Added option to configure default matchers for objects `\Mockery::getConfiguration()->setDefaultMatcher($class, $matcherClass)` #1120 ## 1.3.5 (2021-09-13) - Fix auto-generated return values with union types #1143 - Adds support for tentative types #1130 - Fixes for PHP 8.1 Support (#1130 and #1140) - Add method that allows defining a set of arguments the mock should yield #1133 - Added option to configure default matchers for objects `\Mockery::getConfiguration()->setDefaultMatcher($class, $matcherClass)` #1120 ## 1.4.3 (2021-02-24) - Fixes calls to fetchMock before initialisation #1113 - Allow shouldIgnoreMissing() to behave in a recursive fashion #1097 - Custom object formatters #766 (Needs Docs) - Fix crash on a union type including null #1106 ## 1.3.4 (2021-02-24) - Fixes calls to fetchMock before initialisation #1113 - Fix crash on a union type including null #1106 ## 1.4.2 (2020-08-11) - Fix array to string conversion in ConstantsPass (#1086) - Fixed nullable PHP 8.0 union types (#1088, #1089) - Fixed support for PHP 8.0 parent type (#1088, #1089) - Fixed PHP 8.0 mixed type support (#1088, #1089) - Fixed PHP 8.0 union return types (#1088, #1089) ## 1.4.1 (2020-07-09) - Allow quick definitions to use 'at least once' expectation `\Mockery::getConfiguration()->getQuickDefinitions()->shouldBeCalledAtLeastOnce(true)` (#1056) - Added provisional support for PHP 8.0 (#1068, #1072,#1079) - Fix mocking methods with iterable return type without specifying a return value (#1075) ## 1.3.3 (2020-08-11) - Fix array to string conversion in ConstantsPass (#1086) - Fixed nullable PHP 8.0 union types (#1088) - Fixed support for PHP 8.0 parent type (#1088) - Fixed PHP 8.0 mixed type support (#1088) - Fixed PHP 8.0 union return types (#1088) ## 1.3.2 (2020-07-09) - Fix mocking with anonymous classes (#1039) - Fix andAnyOthers() to properly match earlier expectations (#1051) - Added provisional support for PHP 8.0 (#1068, #1072,#1079) - Fix mocking methods with iterable return type without specifying a return value (#1075) ## 1.4.0 (2020-05-19) - Fix mocking with anonymous classes (#1039) - Fix andAnyOthers() to properly match earlier expectations (#1051) - Drops support for PHP < 7.3 and PHPUnit < 8 (#1059) ## 1.3.1 (2019-12-26) - Revert improved exception debugging due to BC breaks (#1032) ## 1.3.0 (2019-11-24) - Added capture `Mockery::capture` convenience matcher (#1020) - Added `andReturnArg` to echo back an argument passed to a an expectation (#992) - Improved exception debugging (#1000) - Fixed `andSet` to not reuse properties between mock objects (#1012) ## 1.2.4 (2019-09-30) - Fix a bug introduced with previous release, for empty method definition lists (#1009) ## 1.2.3 (2019-08-07) - Allow mocking classes that have allows and expects methods (#868) - Allow passing thru __call method in all mock types (experimental) (#969) - Add support for `!` to blacklist methods (#959) - Added `withSomeOfArgs` to partial match a list of args (#967) - Fix chained demeter calls with type hint (#956) ## 1.2.2 (2019-02-13) - Fix a BC breaking change for PHP 5.6/PHPUnit 5.7.27 (#947) ## 1.2.1 (2019-02-07) - Support for PHPUnit 8 (#942) - Allow mocking static methods called on instance (#938) ## 1.2.0 (2018-10-02) - Starts counting default expectations towards count (#910) - Adds workaround for some HHVM return types (#909) - Adds PhpStorm metadata support for autocomplete etc (#904) - Further attempts to support multiple PHPUnit versions (#903) - Allows setting constructor expectations on instance mocks (#900) - Adds workaround for HHVM memoization decorator (#893) - Adds experimental support for callable spys (#712) ## 1.1.0 (2018-05-08) - Allows use of string method names in allows and expects (#794) - Finalises allows and expects syntax in API (#799) - Search for handlers in a case instensitive way (#801) - Deprecate allowMockingMethodsUnnecessarily (#808) - Fix risky tests (#769) - Fix namespace in TestListener (#812) - Fixed conflicting mock names (#813) - Clean elses (#819) - Updated protected method mocking exception message (#826) - Map of constants to mock (#829) - Simplify foreach with `in_array` function (#830) - Typehinted return value on Expectation#verify. (#832) - Fix shouldNotHaveReceived with HigherOrderMessage (#842) - Deprecates shouldDeferMissing (#839) - Adds support for return type hints in Demeter chains (#848) - Adds shouldNotReceive to composite expectation (#847) - Fix internal error when using --static-backup (#845) - Adds `andAnyOtherArgs` as an optional argument matcher (#860) - Fixes namespace qualifying with namespaced named mocks (#872) - Added possibility to add Constructor-Expections on hard dependencies, read: Mockery::mock('overload:...') (#781) ## 1.0.0 (2017-09-06) - Destructors (`__destruct`) are stubbed out where it makes sense - Allow passing a closure argument to `withArgs()` to validate multiple arguments at once. - `Mockery\Adapter\Phpunit\TestListener` has been rewritten because it incorrectly marked some tests as risky. It will no longer verify mock expectations but instead check that tests do that themselves. PHPUnit 6 is required if you want to use this fail safe. - Removes SPL Class Loader - Removed object recorder feature - Bumped minimum PHP version to 5.6 - `andThrow` will now throw anything `\Throwable` - Adds `allows` and `expects` syntax - Adds optional global helpers for `mock`, `namedMock` and `spy` - Adds ability to create objects using traits - `Mockery\Matcher\MustBe` was deprecated - Marked `Mockery\MockInterface` as internal - Subset matcher matches recursively - BC BREAK - Spies return `null` by default from ignored (non-mocked) methods with nullable return type - Removed extracting getter methods of object instances - BC BREAK - Remove implicit regex matching when trying to match string arguments, introduce `\Mockery::pattern()` when regex matching is needed - Fix Mockery not getting closed in cases of failing test cases - Fix Mockery not setting properties on overloaded instance mocks - BC BREAK - Fix Mockery not trying default expectations if there is any concrete expectation - BC BREAK - Mockery's PHPUnit integration will mark a test as risky if it thinks one it's exceptions has been swallowed in PHPUnit > 5.7.6. Use `$e->dismiss()` to dismiss. ## 0.9.4 (XXXX-XX-XX) - `shouldIgnoreMissing` will respect global `allowMockingNonExistentMethods` config - Some support for variadic parameters - Hamcrest is now a required dependency - Instance mocks now respect `shouldIgnoreMissing` call on control instance - This will be the *last version to support PHP 5.3* - Added `Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration` trait - Added `makePartial` to `Mockery\MockInterface` as it was missing ## 0.9.3 (2014-12-22) - Added a basic spy implementation - Added `Mockery\Adapter\Phpunit\MockeryTestCase` for more reliable PHPUnit integration ## 0.9.2 (2014-09-03) - Some workarounds for the serialisation problems created by changes to PHP in 5.5.13, 5.4.29, 5.6. - Demeter chains attempt to reuse doubles as they see fit, so for foo->bar and foo->baz, we'll attempt to use the same foo ## 0.9.1 (2014-05-02) - Allow specifying consecutive exceptions to be thrown with `andThrowExceptions` - Allow specifying methods which can be mocked when using `Mockery\Configuration::allowMockingNonExistentMethods(false)` with `Mockery\MockInterface::shouldAllowMockingMethod($methodName)` - Added andReturnSelf method: `$mock->shouldReceive("foo")->andReturnSelf()` - `shouldIgnoreMissing` now takes an optional value that will be return instead of null, e.g. `$mock->shouldIgnoreMissing($mock)` ## 0.9.0 (2014-02-05) - Allow mocking classes with final __wakeup() method - Quick definitions are now always `byDefault` - Allow mocking of protected methods with `shouldAllowMockingProtectedMethods` - Support official Hamcrest package - Generator completely rewritten - Easily create named mocks with namedMock PKZOGGmockery/composer.locknuW+A{ "_readme": [ "This file locks the dependencies of your project to a known state", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], "content-hash": "e70f68192a56a148f93ad7a1c0779be3", "packages": [ { "name": "hamcrest/hamcrest-php", "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", "shasum": "" }, "require": { "php": "^5.3|^7.0|^8.0" }, "replace": { "cordoval/hamcrest-php": "*", "davedevelopment/hamcrest-php": "*", "kodova/hamcrest-php": "*" }, "require-dev": { "phpunit/php-file-iterator": "^1.4 || ^2.0", "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.1-dev" } }, "autoload": { "classmap": [ "hamcrest" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "description": "This is the PHP port of Hamcrest Matchers", "keywords": [ "test" ], "support": { "issues": "https://github.com/hamcrest/hamcrest-php/issues", "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" }, "time": "2020-07-09T08:09:16+00:00" } ], "packages-dev": [ { "name": "doctrine/instantiator", "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.16 || ^1", "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", "homepage": "https://ocramius.github.io/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", "type": "custom" }, { "url": "https://www.patreon.com/phpdoctrine", "type": "patreon" }, { "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", "type": "tidelift" } ], "time": "2022-12-30T00:15:36+00:00" }, { "name": "myclabs/deep-copy", "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { "doctrine/collections": "<1.6.8", "doctrine/common": "<2.13.3 || >=3,<3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { "files": [ "src/DeepCopy/deep_copy.php" ], "psr-4": { "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Create deep copies (clones) of your objects", "keywords": [ "clone", "copy", "duplicate", "object", "object graph" ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", "type": "tidelift" } ], "time": "2023-03-08T13:26:56+00:00" }, { "name": "nikic/php-parser", "version": "v4.18.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": ">=7.0" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" ], "type": "library", "extra": { "branch-alias": { "dev-master": "4.9-dev" } }, "autoload": { "psr-4": { "PhpParser\\": "lib/PhpParser" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Nikita Popov" } ], "description": "A PHP parser written in PHP", "keywords": [ "parser", "php" ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" }, "time": "2023-12-10T21:03:43+00:00" }, { "name": "phar-io/manifest", "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" }, { "name": "Sebastian Heuer", "email": "sebastian@phpeople.de", "role": "Developer" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "Developer" } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", "source": "https://github.com/phar-io/manifest/tree/2.0.3" }, "time": "2021-07-20T11:28:43+00:00" }, { "name": "phar-io/version", "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" }, { "name": "Sebastian Heuer", "email": "sebastian@phpeople.de", "role": "Developer" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "Developer" } ], "description": "Library for handling version information and constraints", "support": { "issues": "https://github.com/phar-io/version/issues", "source": "https://github.com/phar-io/version/tree/3.2.1" }, "time": "2022-02-21T01:04:05+00:00" }, { "name": "phpunit/php-code-coverage", "version": "9.2.30", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089", "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", "sebastian/code-unit-reverse-lookup": "^2.0.2", "sebastian/complexity": "^2.0", "sebastian/environment": "^5.1.2", "sebastian/lines-of-code": "^1.0.3", "sebastian/version": "^3.0.1", "theseer/tokenizer": "^1.2.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { "branch-alias": { "dev-master": "9.2-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ "coverage", "testing", "xunit" ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-12-22T06:47:57+00:00" }, { "name": "phpunit/php-file-iterator", "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ "filesystem", "iterator" ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2021-12-02T12:48:52+00:00" }, { "name": "phpunit/php-invoker", "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "ext-pcntl": "*", "phpunit/phpunit": "^9.3" }, "suggest": { "ext-pcntl": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.1-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Invoke callables with a timeout", "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ "process" ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T05:58:55+00:00" }, { "name": "phpunit/php-text-template", "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Simple template engine.", "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ "template" ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T05:33:50+00:00" }, { "name": "phpunit/php-timer", "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Utility class for timing", "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ "timer" ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:16:10+00:00" }, { "name": "phpunit/phpunit", "version": "9.6.17", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1a156980d78a6666721b7e8e8502fe210b587fcd", "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd", "shasum": "" }, "require": { "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", "myclabs/deep-copy": "^1.10.1", "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=7.3", "phpunit/php-code-coverage": "^9.2.28", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", "phpunit/php-timer": "^5.0.2", "sebastian/cli-parser": "^1.0.1", "sebastian/code-unit": "^1.0.6", "sebastian/comparator": "^4.0.8", "sebastian/diff": "^4.0.3", "sebastian/environment": "^5.1.3", "sebastian/exporter": "^4.0.5", "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", "sebastian/type": "^3.2", "sebastian/version": "^3.0.2" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files", "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "bin": [ "phpunit" ], "type": "library", "extra": { "branch-alias": { "dev-master": "9.6-dev" } }, "autoload": { "files": [ "src/Framework/Assert/Functions.php" ], "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "The PHP Unit Testing framework.", "homepage": "https://phpunit.de/", "keywords": [ "phpunit", "testing", "xunit" ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.17" }, "funding": [ { "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { "url": "https://github.com/sebastianbergmann", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], "time": "2024-02-23T13:14:51+00:00" }, { "name": "sebastian/cli-parser", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library for parsing CLI options", "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T06:08:49+00:00" }, { "name": "sebastian/code-unit", "version": "1.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Collection of value objects that represent the PHP code units", "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:08:54+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T05:30:19+00:00" }, { "name": "sebastian/comparator", "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/diff": "^4.0", "sebastian/exporter": "^4.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" } ], "description": "Provides the functionality to compare PHP values for equality", "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", "equality" ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", "shasum": "" }, "require": { "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library for calculating the complexity of PHP code units", "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-12-22T06:19:30+00:00" }, { "name": "sebastian/diff", "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3", "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ "diff", "udiff", "unidiff", "unified diff" ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-05-07T05:35:17+00:00" }, { "name": "sebastian/environment", "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "suggest": { "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.1-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides functionality to handle HHVM/PHP environments", "homepage": "http://www.github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", "hhvm" ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Adam Harvey", "email": "aharvey@php.net" }, { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2022-09-14T06:03:37+00:00" }, { "name": "sebastian/global-state", "version": "5.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", "reference": "bde739e7565280bda77be70044ac1047bc007e34" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", "reference": "bde739e7565280bda77be70044ac1047bc007e34", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/object-reflector": "^2.0", "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-dom": "*", "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Snapshotting of global state", "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-08-02T09:26:13+00:00" }, { "name": "sebastian/lines-of-code", "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", "shasum": "" }, "require": { "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library for counting the lines of code in PHP source code", "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-12-22T06:20:34+00:00" }, { "name": "sebastian/object-enumerator", "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/object-reflector": "^2.0", "sebastian/recursion-context": "^4.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:12:34+00:00" }, { "name": "sebastian/object-reflector", "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:14:26+00:00" }, { "name": "sebastian/recursion-context", "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides functionality to recursively process PHP variables", "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { "issues": "https://github.com/sebastianbergmann/resource-operations/issues", "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T06:45:17+00:00" }, { "name": "sebastian/type", "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.5" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.2-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-02-03T06:13:03+00:00" }, { "name": "sebastian/version", "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { "php": ">=7.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T06:39:44+00:00" }, { "name": "symplify/easy-coding-standard", "version": "12.1.14", "source": { "type": "git", "url": "https://github.com/easy-coding-standard/easy-coding-standard.git", "reference": "e3c4a241ee36704f7cf920d5931f39693e64afd5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/e3c4a241ee36704f7cf920d5931f39693e64afd5", "reference": "e3c4a241ee36704f7cf920d5931f39693e64afd5", "shasum": "" }, "require": { "php": ">=7.2" }, "conflict": { "friendsofphp/php-cs-fixer": "<3.46", "phpcsstandards/php_codesniffer": "<3.8", "symplify/coding-standard": "<12.1" }, "bin": [ "bin/ecs" ], "type": "library", "autoload": { "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Use Coding Standard with 0-knowledge of PHP-CS-Fixer and PHP_CodeSniffer", "keywords": [ "Code style", "automation", "fixer", "static analysis" ], "support": { "issues": "https://github.com/easy-coding-standard/easy-coding-standard/issues", "source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.1.14" }, "funding": [ { "url": "https://www.paypal.me/rectorphp", "type": "custom" }, { "url": "https://github.com/tomasvotruba", "type": "github" } ], "time": "2024-02-23T13:10:40+00:00" }, { "name": "theseer/tokenizer", "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", "source": "https://github.com/theseer/tokenizer/tree/1.2.2" }, "funding": [ { "url": "https://github.com/theseer", "type": "github" } ], "time": "2023-11-20T00:12:19+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=7.3", "lib-pcre": ">=7.0" }, "platform-dev": [], "platform-overrides": { "php": "7.3.999" }, "plugin-api-version": "2.6.0" } PKZUR/mockery/library/Mockery/ReceivedMethodCalls.phpnuW+AmethodCalls[] = $methodCall; } public function verify(Expectation $expectation) { foreach ($this->methodCalls as $methodCall) { if ($methodCall->getMethod() !== $expectation->getName()) { continue; } if (! $expectation->matchArgs($methodCall->getArgs())) { continue; } $expectation->verifyCall($methodCall->getArgs()); } $expectation->verify(); } } PKZ)5mockery/library/Mockery/ExpectsHigherOrderMessage.phpnuW+Aonce(); } } PKZss mockery/library/Mockery/Mock.phpnuW+A_mockery_container = $container; if (!is_null($partialObject)) { $this->_mockery_partial = $partialObject; } if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()) { foreach ($this->mockery_getMethods() as $method) { if ($method->isPublic()) { $this->_mockery_mockableMethods[] = $method->getName(); } } } $this->_mockery_instanceMock = $instanceMock; $this->_mockery_parentClass = get_parent_class($this); } /** * Set expected method calls * * @param string ...$methodNames one or many methods that are expected to be called in this mock * * @return ExpectationInterface|Expectation|HigherOrderMessage */ public function shouldReceive(...$methodNames) { if ($methodNames === []) { return new HigherOrderMessage($this, 'shouldReceive'); } foreach ($methodNames as $method) { if ('' === $method) { throw new \InvalidArgumentException('Received empty method name'); } } $self = $this; $allowMockingProtectedMethods = $this->_mockery_allowMockingProtectedMethods; return \Mockery::parseShouldReturnArgs( $this, $methodNames, static function ($method) use ($self, $allowMockingProtectedMethods) { $rm = $self->mockery_getMethod($method); if ($rm) { if ($rm->isPrivate()) { throw new \InvalidArgumentException($method . '() cannot be mocked as it is a private method'); } if (!$allowMockingProtectedMethods && $rm->isProtected()) { throw new \InvalidArgumentException($method . '() cannot be mocked as it is a protected method and mocking protected methods is not enabled for the currently used mock object. Use shouldAllowMockingProtectedMethods() to enable mocking of protected methods.'); } } $director = $self->mockery_getExpectationsFor($method); if (!$director) { $director = new ExpectationDirector($method, $self); $self->mockery_setExpectationsFor($method, $director); } $expectation = new Expectation($self, $method); $director->addExpectation($expectation); return $expectation; } ); } // start method allows /** * @param mixed $something String method name or map of method => return * @return self|ExpectationInterface|Expectation|HigherOrderMessage */ public function allows($something = []) { if (is_string($something)) { return $this->shouldReceive($something); } if (empty($something)) { return $this->shouldReceive(); } foreach ($something as $method => $returnValue) { $this->shouldReceive($method)->andReturn($returnValue); } return $this; } // end method allows // start method expects /** /** * @param mixed $something String method name (optional) * @return ExpectationInterface|Expectation|ExpectsHigherOrderMessage */ public function expects($something = null) { if (is_string($something)) { return $this->shouldReceive($something)->once(); } return new ExpectsHigherOrderMessage($this); } // end method expects /** * Shortcut method for setting an expectation that a method should not be called. * * @param string ...$methodNames one or many methods that are expected not to be called in this mock * @return ExpectationInterface|Expectation|HigherOrderMessage */ public function shouldNotReceive(...$methodNames) { if ($methodNames === []) { return new HigherOrderMessage($this, 'shouldNotReceive'); } $expectation = call_user_func_array(function (string $methodNames) { return $this->shouldReceive($methodNames); }, $methodNames); $expectation->never(); return $expectation; } /** * Allows additional methods to be mocked that do not explicitly exist on mocked class * * @param string $method name of the method to be mocked * @return Mock|MockInterface|LegacyMockInterface */ public function shouldAllowMockingMethod($method) { $this->_mockery_mockableMethods[] = $method; return $this; } /** * Set mock to ignore unexpected methods and return Undefined class * @param mixed $returnValue the default return value for calls to missing functions on this mock * @param bool $recursive Specify if returned mocks should also have shouldIgnoreMissing set * @return static */ public function shouldIgnoreMissing($returnValue = null, $recursive = false) { $this->_mockery_ignoreMissing = true; $this->_mockery_ignoreMissingRecursive = $recursive; $this->_mockery_defaultReturnValue = $returnValue; return $this; } public function asUndefined() { $this->_mockery_ignoreMissing = true; $this->_mockery_defaultReturnValue = new Undefined(); return $this; } /** * @return static */ public function shouldAllowMockingProtectedMethods() { if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()) { foreach ($this->mockery_getMethods() as $method) { if ($method->isProtected()) { $this->_mockery_mockableMethods[] = $method->getName(); } } } $this->_mockery_allowMockingProtectedMethods = true; return $this; } /** * Set mock to defer unexpected methods to it's parent * * This is particularly useless for this class, as it doesn't have a parent, * but included for completeness * * @deprecated 2.0.0 Please use makePartial() instead * * @return static */ public function shouldDeferMissing() { return $this->makePartial(); } /** * Set mock to defer unexpected methods to it's parent * * It was an alias for shouldDeferMissing(), which will be removed * in 2.0.0. * * @return static */ public function makePartial() { $this->_mockery_deferMissing = true; return $this; } /** * In the event shouldReceive() accepting one or more methods/returns, * this method will switch them from normal expectations to default * expectations * * @return self */ public function byDefault() { foreach ($this->_mockery_expectations as $director) { $exps = $director->getExpectations(); foreach ($exps as $exp) { $exp->byDefault(); } } return $this; } /** * Capture calls to this mock */ public function __call($method, array $args) { return $this->_mockery_handleMethodCall($method, $args); } public static function __callStatic($method, array $args) { return self::_mockery_handleStaticMethodCall($method, $args); } /** * Forward calls to this magic method to the __call method */ #[\ReturnTypeWillChange] public function __toString() { return $this->__call('__toString', []); } /** * Iterate across all expectation directors and validate each * * @throws Exception * @return void */ public function mockery_verify() { if ($this->_mockery_verified) { return; } if (property_exists($this, '_mockery_ignoreVerification') && $this->_mockery_ignoreVerification !== null && $this->_mockery_ignoreVerification == true) { return; } $this->_mockery_verified = true; foreach ($this->_mockery_expectations as $director) { $director->verify(); } } /** * Gets a list of exceptions thrown by this mock * * @return array */ public function mockery_thrownExceptions() { return $this->_mockery_thrownExceptions; } /** * Tear down tasks for this mock * * @return void */ public function mockery_teardown() { } /** * Fetch the next available allocation order number * * @return int */ public function mockery_allocateOrder() { ++$this->_mockery_allocatedOrder; return $this->_mockery_allocatedOrder; } /** * Set ordering for a group * * @param mixed $group * @param int $order */ public function mockery_setGroup($group, $order) { $this->_mockery_groups[$group] = $order; } /** * Fetch array of ordered groups * * @return array */ public function mockery_getGroups() { return $this->_mockery_groups; } /** * Set current ordered number * * @param int $order */ public function mockery_setCurrentOrder($order) { $this->_mockery_currentOrder = $order; return $this->_mockery_currentOrder; } /** * Get current ordered number * * @return int */ public function mockery_getCurrentOrder() { return $this->_mockery_currentOrder; } /** * Validate the current mock's ordering * * @param string $method * @param int $order * @throws \Mockery\Exception * @return void */ public function mockery_validateOrder($method, $order) { if ($order < $this->_mockery_currentOrder) { $exception = new InvalidOrderException( 'Method ' . self::class . '::' . $method . '()' . ' called out of order: expected order ' . $order . ', was ' . $this->_mockery_currentOrder ); $exception->setMock($this) ->setMethodName($method) ->setExpectedOrder($order) ->setActualOrder($this->_mockery_currentOrder); throw $exception; } $this->mockery_setCurrentOrder($order); } /** * Gets the count of expectations for this mock * * @return int */ public function mockery_getExpectationCount() { $count = $this->_mockery_expectations_count; foreach ($this->_mockery_expectations as $director) { $count += $director->getExpectationCount(); } return $count; } /** * Return the expectations director for the given method * * @var string $method * @return ExpectationDirector|null */ public function mockery_setExpectationsFor($method, ExpectationDirector $director) { $this->_mockery_expectations[$method] = $director; } /** * Return the expectations director for the given method * * @var string $method * @return ExpectationDirector|null */ public function mockery_getExpectationsFor($method) { if (isset($this->_mockery_expectations[$method])) { return $this->_mockery_expectations[$method]; } } /** * Find an expectation matching the given method and arguments * * @var string $method * @var array $args * @return Expectation|null */ public function mockery_findExpectation($method, array $args) { if (!isset($this->_mockery_expectations[$method])) { return null; } $director = $this->_mockery_expectations[$method]; return $director->findExpectation($args); } /** * Return the container for this mock * * @return Container */ public function mockery_getContainer() { return $this->_mockery_container; } /** * Return the name for this mock * * @return string */ public function mockery_getName() { return self::class; } /** * @return array */ public function mockery_getMockableProperties() { return $this->_mockery_mockableProperties; } public function __isset($name) { if (false !== stripos($name, '_mockery_')) { return false; } if (!$this->_mockery_parentClass) { return false; } if (!method_exists($this->_mockery_parentClass, '__isset')) { return false; } return call_user_func($this->_mockery_parentClass . '::__isset', $name); } public function mockery_getExpectations() { return $this->_mockery_expectations; } /** * Calls a parent class method and returns the result. Used in a passthru * expectation where a real return value is required while still taking * advantage of expectation matching and call count verification. * * @param string $name * @param array $args * @return mixed */ public function mockery_callSubjectMethod($name, array $args) { if (!method_exists($this, $name) && $this->_mockery_parentClass && method_exists($this->_mockery_parentClass, '__call')) { return call_user_func($this->_mockery_parentClass . '::__call', $name, $args); } return call_user_func_array($this->_mockery_parentClass . '::' . $name, $args); } /** * @return string[] */ public function mockery_getMockableMethods() { return $this->_mockery_mockableMethods; } /** * @return bool */ public function mockery_isAnonymous() { $rfc = new \ReflectionClass($this); // PHP 8 has Stringable interface $interfaces = array_filter($rfc->getInterfaces(), static function ($i) { return $i->getName() !== 'Stringable'; }); return false === $rfc->getParentClass() && 2 === count($interfaces); } public function mockery_isInstance() { return $this->_mockery_instanceMock; } public function __wakeup() { /** * This does not add __wakeup method support. It's a blind method and any * expected __wakeup work will NOT be performed. It merely cuts off * annoying errors where a __wakeup exists but is not essential when * mocking */ } public function __destruct() { /** * Overrides real class destructor in case if class was created without original constructor */ } public function mockery_getMethod($name) { foreach ($this->mockery_getMethods() as $method) { if ($method->getName() == $name) { return $method; } } return null; } /** * @param string $name Method name. * * @return mixed Generated return value based on the declared return value of the named method. */ public function mockery_returnValueForMethod($name) { $rm = $this->mockery_getMethod($name); if ($rm === null) { return null; } $returnType = Reflector::getSimplestReturnType($rm); switch ($returnType) { case null: return null; case 'string': return ''; case 'int': return 0; case 'float': return 0.0; case 'bool': return false; case 'true': return true; case 'false': return false; case 'array': case 'iterable': return []; case 'callable': case '\Closure': return static function () : void { }; case '\Traversable': case '\Generator': $generator = static function () { yield; }; return $generator(); case 'void': return null; case 'static': return $this; case 'object': $mock = \Mockery::mock(); if ($this->_mockery_ignoreMissingRecursive) { $mock->shouldIgnoreMissing($this->_mockery_defaultReturnValue, true); } return $mock; default: $mock = \Mockery::mock($returnType); if ($this->_mockery_ignoreMissingRecursive) { $mock->shouldIgnoreMissing($this->_mockery_defaultReturnValue, true); } return $mock; } } public function shouldHaveReceived($method = null, $args = null) { if ($method === null) { return new HigherOrderMessage($this, 'shouldHaveReceived'); } $expectation = new VerificationExpectation($this, $method); if (null !== $args) { $expectation->withArgs($args); } $expectation->atLeast()->once(); $director = new VerificationDirector($this->_mockery_getReceivedMethodCalls(), $expectation); ++$this->_mockery_expectations_count; $director->verify(); return $director; } public function shouldHaveBeenCalled() { return $this->shouldHaveReceived('__invoke'); } public function shouldNotHaveReceived($method = null, $args = null) { if ($method === null) { return new HigherOrderMessage($this, 'shouldNotHaveReceived'); } $expectation = new VerificationExpectation($this, $method); if (null !== $args) { $expectation->withArgs($args); } $expectation->never(); $director = new VerificationDirector($this->_mockery_getReceivedMethodCalls(), $expectation); ++$this->_mockery_expectations_count; $director->verify(); return null; } public function shouldNotHaveBeenCalled(?array $args = null) { return $this->shouldNotHaveReceived('__invoke', $args); } protected static function _mockery_handleStaticMethodCall($method, array $args) { $associatedRealObject = \Mockery::fetchMock(self::class); try { return $associatedRealObject->__call($method, $args); } catch (BadMethodCallException $badMethodCallException) { throw new BadMethodCallException( 'Static method ' . $associatedRealObject->mockery_getName() . '::' . $method . '() does not exist on this mock object', 0, $badMethodCallException ); } } protected function _mockery_getReceivedMethodCalls() { return $this->_mockery_receivedMethodCalls ?: $this->_mockery_receivedMethodCalls = new ReceivedMethodCalls(); } /** * Called when an instance Mock was created and its constructor is getting called * * @see \Mockery\Generator\StringManipulation\Pass\InstanceMockPass * @param array $args */ protected function _mockery_constructorCalled(array $args) { if (!isset($this->_mockery_expectations['__construct']) /* _mockery_handleMethodCall runs the other checks */) { return; } $this->_mockery_handleMethodCall('__construct', $args); } protected function _mockery_findExpectedMethodHandler($method) { if (isset($this->_mockery_expectations[$method])) { return $this->_mockery_expectations[$method]; } $lowerCasedMockeryExpectations = array_change_key_case($this->_mockery_expectations, CASE_LOWER); $lowerCasedMethod = strtolower($method); return $lowerCasedMockeryExpectations[$lowerCasedMethod] ?? null; } protected function _mockery_handleMethodCall($method, array $args) { $this->_mockery_getReceivedMethodCalls()->push(new MethodCall($method, $args)); $rm = $this->mockery_getMethod($method); if ($rm && $rm->isProtected() && !$this->_mockery_allowMockingProtectedMethods) { if ($rm->isAbstract()) { return; } try { $prototype = $rm->getPrototype(); if ($prototype->isAbstract()) { return; } } catch (\ReflectionException $re) { // noop - there is no hasPrototype method } if (null === $this->_mockery_parentClass) { $this->_mockery_parentClass = get_parent_class($this); } return call_user_func_array($this->_mockery_parentClass . '::' . $method, $args); } $handler = $this->_mockery_findExpectedMethodHandler($method); if ($handler !== null && !$this->_mockery_disableExpectationMatching) { try { return $handler->call($args); } catch (NoMatchingExpectationException $e) { if (!$this->_mockery_ignoreMissing && !$this->_mockery_deferMissing) { throw $e; } } } if (!is_null($this->_mockery_partial) && (method_exists($this->_mockery_partial, $method) || method_exists($this->_mockery_partial, '__call'))) { return $this->_mockery_partial->{$method}(...$args); } if ($this->_mockery_deferMissing && is_callable($this->_mockery_parentClass . '::' . $method) && (!$this->hasMethodOverloadingInParentClass() || ($this->_mockery_parentClass && method_exists($this->_mockery_parentClass, $method)))) { return call_user_func_array($this->_mockery_parentClass . '::' . $method, $args); } if ($this->_mockery_deferMissing && $this->_mockery_parentClass && method_exists($this->_mockery_parentClass, '__call')) { return call_user_func($this->_mockery_parentClass . '::__call', $method, $args); } if ($method === '__toString') { // __toString is special because we force its addition to the class API regardless of the // original implementation. Thus, we should always return a string rather than honor // _mockery_ignoreMissing and break the API with an error. return sprintf('%s#%s', self::class, spl_object_hash($this)); } if ($this->_mockery_ignoreMissing && (\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed() || (!is_null($this->_mockery_partial) && method_exists($this->_mockery_partial, $method)) || is_callable($this->_mockery_parentClass . '::' . $method))) { if ($this->_mockery_defaultReturnValue instanceof Undefined) { return $this->_mockery_defaultReturnValue->{$method}(...$args); } if (null === $this->_mockery_defaultReturnValue) { return $this->mockery_returnValueForMethod($method); } return $this->_mockery_defaultReturnValue; } $message = 'Method ' . self::class . '::' . $method . '() does not exist on this mock object'; if (!is_null($rm)) { $message = 'Received ' . self::class . '::' . $method . '(), but no expectations were specified'; } $bmce = new BadMethodCallException($message); $this->_mockery_thrownExceptions[] = $bmce; throw $bmce; } /** * Uses reflection to get the list of all * methods within the current mock object * * @return array */ protected function mockery_getMethods() { if (static::$_mockery_methods && \Mockery::getConfiguration()->reflectionCacheEnabled()) { return static::$_mockery_methods; } if ($this->_mockery_partial !== null) { $reflected = new \ReflectionObject($this->_mockery_partial); } else { $reflected = new \ReflectionClass($this); } return static::$_mockery_methods = $reflected->getMethods(); } private function hasMethodOverloadingInParentClass() { // if there's __call any name would be callable return is_callable($this->_mockery_parentClass . '::aFunctionNameThatNoOneWouldEverUseInRealLife12345'); } /** * @return array */ private function getNonPublicMethods() { return array_map( static function ($method) { return $method->getName(); }, array_filter($this->mockery_getMethods(), static function ($method) { return !$method->isPublic(); }) ); } } PKZGM*mockery/library/Mockery/Matcher/IsSame.phpnuW+A'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return $this->_expected === $actual; } } PKZ:bm*mockery/library/Mockery/Matcher/HasKey.phpnuW+A', $this->_expected); } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { if (! is_array($actual) && ! $actual instanceof ArrayAccess) { return false; } return array_key_exists($this->_expected, (array) $actual); } } PKZrFH'mockery/library/Mockery/Matcher/Any.phpnuW+A'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return true; } } PKZF.gg(mockery/library/Mockery/Matcher/Type.phpnuW+A_expected) . '>'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { $function = $this->_expected === 'real' ? 'is_float' : 'is_' . strtolower($this->_expected); if (function_exists($function)) { return $function($actual); } if (! is_string($this->_expected)) { return false; } if (class_exists($this->_expected) || interface_exists($this->_expected)) { return $actual instanceof $this->_expected; } return false; } } PKZL[>3mockery/library/Mockery/Matcher/AndAnyOtherArgs.phpnuW+A'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return true; } } PKZЀ8mockery/library/Mockery/Matcher/MultiArgumentClosure.phpnuW+A'; } /** * Check if the actual value matches the expected. * Actual passed by reference to preserve reference trail (where applicable) * back to the original method parameter. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return ($this->_expected)(...$actual) === true; } } PKZ )PP'mockery/library/Mockery/Matcher/Not.phpnuW+A'; } /** * Check if the actual value does not match the expected (in this * case it's specifically NOT expected). * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return $actual !== $this->_expected; } } PKZgFb+mockery/library/Mockery/Matcher/IsEqual.phpnuW+A'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return $this->_expected == $actual; } } PKZcA,mockery/library/Mockery/Matcher/NotAnyOf.phpnuW+A'; } /** * Check if the actual value does not match the expected (in this * case it's specifically NOT expected). * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { foreach ($this->_expected as $exp) { if ($actual === $exp || $actual == $exp) { return false; } } return true; } } PKZ *mockery/library/Mockery/Matcher/Subset.phpnuW+Aexpected = $expected; $this->strict = $strict; } /** * Return a string representation of this Matcher * * @return string */ public function __toString() { return 'formatArray($this->expected) . '>'; } /** * @param array $expected Expected subset of data * * @return Subset */ public static function loose(array $expected) { return new static($expected, false); } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { if (! is_array($actual)) { return false; } if ($this->strict) { return $actual === array_replace_recursive($actual, $this->expected); } return $actual == array_replace_recursive($actual, $this->expected); } /** * @param array $expected Expected subset of data * * @return Subset */ public static function strict(array $expected) { return new static($expected, true); } /** * Recursively format an array into the string representation for this matcher * * @return string */ protected function formatArray(array $array) { $elements = []; foreach ($array as $k => $v) { $elements[] = $k . '=' . (is_array($v) ? $this->formatArray($v) : (string) $v); } return '[' . implode(', ', $elements) . ']'; } } PKZCUU+mockery/library/Mockery/Matcher/Pattern.phpnuW+A'; } /** * Check if the actual value matches the expected pattern. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return preg_match($this->_expected, (string) $actual) >= 1; } } PKZhj>XX,mockery/library/Mockery/Matcher/Contains.phpnuW+A_expected as $v) { $elements[] = (string) $v; } return ''; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { $values = array_values($actual); foreach ($this->_expected as $exp) { $match = false; foreach ($values as $val) { if ($exp === $val || $exp == $val) { $match = true; break; } } if ($match === false) { return false; } } return true; } } PKZ#h,mockery/library/Mockery/Matcher/HasValue.phpnuW+A_expected . ']>'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { if (! is_array($actual) && ! $actual instanceof ArrayAccess) { return false; } return in_array($this->_expected, (array) $actual, true); } } PKZE<*mockery/library/Mockery/Matcher/NoArgs.phpnuW+A'; } /** * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return count($actual) === 0; } } PKZ_+++mockery/library/Mockery/Matcher/Closure.phpnuW+A'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return ($this->_expected)($actual) === true; } } PKZ*8M*mockery/library/Mockery/Matcher/MustBe.phpnuW+A'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { if (! is_object($actual)) { return $this->_expected === $actual; } return $this->_expected == $actual; } } PKZ;QKK4mockery/library/Mockery/Matcher/MatcherInterface.phpnuW+A_expected) . ']>'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { if (! is_object($actual)) { return false; } foreach ($this->_expected as $method) { if (! method_exists($actual, $method)) { return false; } } return true; } } PKZ+mockery/library/Mockery/Matcher/AnyArgs.phpnuW+A'; } /** * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return true; } } PKZ-Ryy)mockery/library/Mockery/Matcher/AnyOf.phpnuW+A'; } /** * Check if the actual value does not match the expected (in this * case it's specifically NOT expected). * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return in_array($actual, $this->_expected, true); } } PKZP,3mockery/library/Mockery/Matcher/MatcherAbstract.phpnuW+A_expected = $expected; } } PKZRPee7mockery/library/Mockery/Matcher/ArgumentListMatcher.phpnuW+Aactual; } /** * @return int */ public function getExpectedCount() { return $this->expected; } /** * @return string */ public function getExpectedCountComparative() { return $this->expectedComparative; } /** * @return string|null */ public function getMethodName() { return $this->method; } /** * @return LegacyMockInterface|null */ public function getMock() { return $this->mockObject; } /** * @throws RuntimeException * @return string|null */ public function getMockName() { $mock = $this->getMock(); if ($mock === null) { return ''; } return $mock->mockery_getName(); } /** * @param int $count * @return self */ public function setActualCount($count) { $this->actual = $count; return $this; } /** * @param int $count * @return self */ public function setExpectedCount($count) { $this->expected = $count; return $this; } /** * @param string $comp * @return self */ public function setExpectedCountComparative($comp) { if (! in_array($comp, ['=', '>', '<', '>=', '<='], true)) { throw new RuntimeException('Illegal comparative for expected call counts set: ' . $comp); } $this->expectedComparative = $comp; return $this; } /** * @param string $name * @return self */ public function setMethodName($name) { $this->method = $name; return $this; } /** * @return self */ public function setMock(LegacyMockInterface $mock) { $this->mockObject = $mock; return $this; } } PKZ ?mockery/library/Mockery/Exception/MockeryExceptionInterface.phpnuW+Aactual; } /** * @return int */ public function getExpectedOrder() { return $this->expected; } /** * @return string|null */ public function getMethodName() { return $this->method; } /** * @return LegacyMockInterface|null */ public function getMock() { return $this->mockObject; } /** * @return string|null */ public function getMockName() { $mock = $this->getMock(); if ($mock === null) { return $mock; } return $mock->mockery_getName(); } /** * @param int $count * * @return self */ public function setActualOrder($count) { $this->actual = $count; return $this; } /** * @param int $count * * @return self */ public function setExpectedOrder($count) { $this->expected = $count; return $this; } /** * @param string $name * * @return self */ public function setMethodName($name) { $this->method = $name; return $this; } /** * @return self */ public function setMock(LegacyMockInterface $mock) { $this->mockObject = $mock; return $this; } } PKZ6mockery/library/Mockery/Exception/RuntimeException.phpnuW+Adismissed = true; // we sometimes stack them $previous = $this->getPrevious(); if (! $previous instanceof self) { return; } $previous->dismiss(); } /** * @return bool */ public function dismissed() { return $this->dismissed; } } PKZ ߯>mockery/library/Mockery/Exception/InvalidArgumentException.phpnuW+A */ protected $actual = []; /** * @var string|null */ protected $method = null; /** * @var LegacyMockInterface|null */ protected $mockObject = null; /** * @return array */ public function getActualArguments() { return $this->actual; } /** * @return string|null */ public function getMethodName() { return $this->method; } /** * @return LegacyMockInterface|null */ public function getMock() { return $this->mockObject; } /** * @return string|null */ public function getMockName() { $mock = $this->getMock(); if ($mock === null) { return $mock; } return $mock->mockery_getName(); } /** * @todo Rename param `count` to `args` * @template TMixed * * @param array $count * @return self */ public function setActualArguments($count) { $this->actual = $count; return $this; } /** * @param string $name * @return self */ public function setMethodName($name) { $this->method = $name; return $this; } /** * @return self */ public function setMock(LegacyMockInterface $mock) { $this->mockObject = $mock; return $this; } } PKZ^ yrr.mockery/library/Mockery/HigherOrderMessage.phpnuW+Amock = $mock; $this->method = $method; } /** * @param string $method * @param array $args * * @return Expectation|ExpectationInterface|HigherOrderMessage */ public function __call($method, $args) { if ($this->method === 'shouldNotHaveReceived') { return $this->mock->{$this->method}($method, $args); } $expectation = $this->mock->{$this->method}($method); return $expectation->withArgs($args); } } PKZN)))mockery/library/Mockery/Configuration.phpnuW+A ['MY_CONST' => 123, 'OTHER_CONST' => 'foo']] * * @var array|scalar>> */ protected $_constantsMap = []; /** * Default argument matchers * * e.g. ['class' => 'matcher'] * * @var array */ protected $_defaultMatchers = []; /** * Parameter map for use with PHP internal classes. * * e.g. ['class' => ['method' => ['param1', 'param2']]] * * @var array>> */ protected $_internalClassParamMap = []; /** * Custom object formatters * * e.g. ['class' => static fn($object) => 'formatted'] * * @var array */ protected $_objectFormatters = []; /** * @var QuickDefinitionsConfiguration */ protected $_quickDefinitionsConfiguration; /** * Boolean assertion is reflection caching enabled or not. It should be * always enabled, except when using PHPUnit's --static-backup option. * * @see https://github.com/mockery/mockery/issues/268 */ protected $_reflectionCacheEnabled = true; public function __construct() { $this->_quickDefinitionsConfiguration = new QuickDefinitionsConfiguration(); } /** * Set boolean to allow/prevent unnecessary mocking of methods * * @param bool $flag * * @return void * * @deprecated since 1.4.0 */ public function allowMockingMethodsUnnecessarily($flag = true) { @trigger_error( sprintf('The %s method is deprecated and will be removed in a future version of Mockery', __METHOD__), E_USER_DEPRECATED ); $this->_allowMockingMethodsUnnecessarily = (bool) $flag; } /** * Set boolean to allow/prevent mocking of non-existent methods * * @param bool $flag * * @return void */ public function allowMockingNonExistentMethods($flag = true) { $this->_allowMockingNonExistentMethod = (bool) $flag; } /** * Disable reflection caching * * It should be always enabled, except when using * PHPUnit's --static-backup option. * * @see https://github.com/mockery/mockery/issues/268 * * @return void */ public function disableReflectionCache() { $this->_reflectionCacheEnabled = false; } /** * Enable reflection caching * * It should be always enabled, except when using * PHPUnit's --static-backup option. * * @see https://github.com/mockery/mockery/issues/268 * * @return void */ public function enableReflectionCache() { $this->_reflectionCacheEnabled = true; } /** * Get the map of constants to be used in the mock generator * * @return array|scalar>> */ public function getConstantsMap() { return $this->_constantsMap; } /** * Get the default matcher for a given class * * @param class-string $class * * @return null|class-string */ public function getDefaultMatcher($class) { $classes = []; $parentClass = $class; do { $classes[] = $parentClass; $parentClass = get_parent_class($parentClass); } while ($parentClass !== false); $classesAndInterfaces = array_merge($classes, class_implements($class)); foreach ($classesAndInterfaces as $type) { if (array_key_exists($type, $this->_defaultMatchers)) { return $this->_defaultMatchers[$type]; } } return null; } /** * Get the parameter map of an internal PHP class method * * @param class-string $class * @param string $method * * @return null|array */ public function getInternalClassMethodParamMap($class, $method) { $class = strtolower($class); $method = strtolower($method); if (! array_key_exists($class, $this->_internalClassParamMap)) { return null; } if (! array_key_exists($method, $this->_internalClassParamMap[$class])) { return null; } return $this->_internalClassParamMap[$class][$method]; } /** * Get the parameter maps of internal PHP classes * * @return array>> */ public function getInternalClassMethodParamMaps() { return $this->_internalClassParamMap; } /** * Get the object formatter for a class * * @param class-string $class * @param Closure $defaultFormatter * * @return Closure */ public function getObjectFormatter($class, $defaultFormatter) { $parentClass = $class; do { $classes[] = $parentClass; $parentClass = get_parent_class($parentClass); } while ($parentClass !== false); $classesAndInterfaces = array_merge($classes, class_implements($class)); foreach ($classesAndInterfaces as $type) { if (array_key_exists($type, $this->_objectFormatters)) { return $this->_objectFormatters[$type]; } } return $defaultFormatter; } /** * Returns the quick definitions configuration */ public function getQuickDefinitions(): QuickDefinitionsConfiguration { return $this->_quickDefinitionsConfiguration; } /** * Return flag indicating whether mocking non-existent methods allowed * * @return bool * * @deprecated since 1.4.0 */ public function mockingMethodsUnnecessarilyAllowed() { @trigger_error( sprintf('The %s method is deprecated and will be removed in a future version of Mockery', __METHOD__), E_USER_DEPRECATED ); return $this->_allowMockingMethodsUnnecessarily; } /** * Return flag indicating whether mocking non-existent methods allowed * * @return bool */ public function mockingNonExistentMethodsAllowed() { return $this->_allowMockingNonExistentMethod; } /** * Is reflection cache enabled? * * @return bool */ public function reflectionCacheEnabled() { return $this->_reflectionCacheEnabled; } /** * Remove all overridden parameter maps from internal PHP classes. * * @return void */ public function resetInternalClassMethodParamMaps() { $this->_internalClassParamMap = []; } /** * Set a map of constants to be used in the mock generator * * e.g. ['MyClass' => ['MY_CONST' => 123, 'ARRAY_CONST' => ['foo', 'bar']]] * * @param array|scalar>> $map * * @return void */ public function setConstantsMap(array $map) { $this->_constantsMap = $map; } /** * @param class-string $class * @param class-string $matcherClass * * @throws InvalidArgumentException * * @return void */ public function setDefaultMatcher($class, $matcherClass) { $isHamcrest = is_a($matcherClass, Matcher::class, true) || is_a($matcherClass, Hamcrest_Matcher::class, true); if ($isHamcrest) { @trigger_error('Hamcrest package has been deprecated and will be removed in 2.0', E_USER_DEPRECATED); } if (! $isHamcrest && ! is_a($matcherClass, MatcherInterface::class, true)) { throw new InvalidArgumentException(sprintf( "Matcher class must implement %s, '%s' given.", MatcherInterface::class, $matcherClass )); } $this->_defaultMatchers[$class] = $matcherClass; } /** * Set a parameter map (array of param signature strings) for the method of an internal PHP class. * * @param class-string $class * @param string $method * @param list $map * * @throws LogicException * * @return void */ public function setInternalClassMethodParamMap($class, $method, array $map) { if (PHP_MAJOR_VERSION > 7) { throw new LogicException( 'Internal class parameter overriding is not available in PHP 8. Incompatible signatures have been reclassified as fatal errors.' ); } $class = strtolower($class); if (! array_key_exists($class, $this->_internalClassParamMap)) { $this->_internalClassParamMap[$class] = []; } $this->_internalClassParamMap[$class][strtolower($method)] = $map; } /** * Set a custom object formatter for a class * * @param class-string $class * @param Closure $formatterCallback * * @return void */ public function setObjectFormatter($class, $formatterCallback) { $this->_objectFormatters[$class] = $formatterCallback; } } PKZS^Bmockery/library/Mockery/CountValidator/CountValidatorInterface.phpnuW+A_limit > $n) { $exception = new InvalidCountException( 'Method ' . (string) $this->_expectation . ' from ' . $this->_expectation->getMock()->mockery_getName() . ' should be called' . PHP_EOL . ' at least ' . $this->_limit . ' times but called ' . $n . ' times.' ); $exception->setMock($this->_expectation->getMock()) ->setMethodName((string) $this->_expectation) ->setExpectedCountComparative('>=') ->setExpectedCount($this->_limit) ->setActualCount($n); throw $exception; } } } PKZT4mockery/library/Mockery/CountValidator/Exception.phpnuW+A_limit !== $n) { $because = $this->_expectation->getExceptionMessage(); $exception = new InvalidCountException( 'Method ' . (string) $this->_expectation . ' from ' . $this->_expectation->getMock()->mockery_getName() . ' should be called' . PHP_EOL . ' exactly ' . $this->_limit . ' times but called ' . $n . ' times.' . ($because ? ' Because ' . $this->_expectation->getExceptionMessage() : '') ); $exception->setMock($this->_expectation->getMock()) ->setMethodName((string) $this->_expectation) ->setExpectedCountComparative('=') ->setExpectedCount($this->_limit) ->setActualCount($n); throw $exception; } } } PKZ{4c@@1mockery/library/Mockery/CountValidator/AtMost.phpnuW+A_limit < $n) { $exception = new InvalidCountException( 'Method ' . (string) $this->_expectation . ' from ' . $this->_expectation->getMock()->mockery_getName() . ' should be called' . PHP_EOL . ' at most ' . $this->_limit . ' times but called ' . $n . ' times.' ); $exception->setMock($this->_expectation->getMock()) ->setMethodName((string) $this->_expectation) ->setExpectedCountComparative('<=') ->setExpectedCount($this->_limit) ->setActualCount($n); throw $exception; } } } PKZ8Amockery/library/Mockery/CountValidator/CountValidatorAbstract.phpnuW+A_expectation = $expectation; $this->_limit = $limit; } /** * Checks if the validator can accept an additional nth call * * @param int $n * * @return bool */ public function isEligible($n) { return $n < $this->_limit; } /** * Validate the call count against this validator * * @param int $n * * @return bool */ abstract public function validate($n); } PKZQb)mockery/library/Mockery/MockInterface.phpnuW+A return * * @return Expectation|ExpectationInterface|HigherOrderMessage|self */ public function allows($something = []); /** * @param mixed $something String method name (optional) * * @return Expectation|ExpectationInterface|ExpectsHigherOrderMessage */ public function expects($something = null); } PKZ&,-mockery/library/Mockery/Loader/EvalLoader.phpnuW+AgetClassName(), false)) { return; } eval('?>' . $definition->getCode()); } } PKZ{(1)mockery/library/Mockery/Loader/Loader.phpnuW+Apath = realpath($path); } public function __destruct() { $files = array_diff(glob($this->path . DIRECTORY_SEPARATOR . 'Mockery_*.php') ?: [], [$this->lastPath]); foreach ($files as $file) { @unlink($file); } } /** * Load the given mock definition * * @return void */ public function load(MockDefinition $definition) { if (class_exists($definition->getClassName(), false)) { return; } $this->lastPath = sprintf('%s%s%s.php', $this->path, DIRECTORY_SEPARATOR, uniqid('Mockery_', false)); file_put_contents($this->lastPath, $definition->getCode()); if (file_exists($this->lastPath)) { require $this->lastPath; } } } PKZXr``'mockery/library/Mockery/Expectation.phpnuW+A_mock = $mock; $this->_name = $name; $this->withAnyArgs(); } /** * Cloning logic */ public function __clone() { $newValidators = []; $countValidators = $this->_countValidators; foreach ($countValidators as $validator) { $newValidators[] = clone $validator; } $this->_countValidators = $newValidators; } /** * Return a string with the method name and arguments formatted * * @return string */ public function __toString() { return Mockery::formatArgs($this->_name, $this->_expectedArgs); } /** * Set a return value, or sequential queue of return values * * @param mixed ...$args * * @return self */ public function andReturn(...$args) { $this->_returnQueue = $args; return $this; } /** * Sets up a closure to return the nth argument from the expected method call * * @param int $index * * @return self */ public function andReturnArg($index) { if (! is_int($index) || $index < 0) { throw new InvalidArgumentException( 'Invalid argument index supplied. Index must be a non-negative integer.' ); } $closure = static function (...$args) use ($index) { if (array_key_exists($index, $args)) { return $args[$index]; } throw new OutOfBoundsException( 'Cannot return an argument value. No argument exists for the index ' . $index ); }; $this->_closureQueue = [$closure]; return $this; } /** * @return self */ public function andReturnFalse() { return $this->andReturn(false); } /** * Return null. This is merely a language construct for Mock describing. * * @return self */ public function andReturnNull() { return $this->andReturn(null); } /** * Set a return value, or sequential queue of return values * * @param mixed ...$args * * @return self */ public function andReturns(...$args) { return $this->andReturn(...$args); } /** * Return this mock, like a fluent interface * * @return self */ public function andReturnSelf() { return $this->andReturn($this->_mock); } /** * @return self */ public function andReturnTrue() { return $this->andReturn(true); } /** * Return a self-returning black hole object. * * @return self */ public function andReturnUndefined() { return $this->andReturn(new Undefined()); } /** * Set a closure or sequence of closures with which to generate return * values. The arguments passed to the expected method are passed to the * closures as parameters. * * @param callable ...$args * * @return self */ public function andReturnUsing(...$args) { $this->_closureQueue = $args; return $this; } /** * Set a sequential queue of return values with an array * * @return self */ public function andReturnValues(array $values) { return $this->andReturn(...$values); } /** * Register values to be set to a public property each time this expectation occurs * * @param string $name * @param array ...$values * * @return self */ public function andSet($name, ...$values) { $this->_setQueue[$name] = $values; return $this; } /** * Set Exception class and arguments to that class to be thrown * * @param string|Throwable $exception * @param string $message * @param int $code * * @return self */ public function andThrow($exception, $message = '', $code = 0, ?\Exception $previous = null) { $this->_throw = true; if (is_object($exception)) { return $this->andReturn($exception); } return $this->andReturn(new $exception($message, $code, $previous)); } /** * Set Exception classes to be thrown * * @return self */ public function andThrowExceptions(array $exceptions) { $this->_throw = true; foreach ($exceptions as $exception) { if (! is_object($exception)) { throw new Exception('You must pass an array of exception objects to andThrowExceptions'); } } return $this->andReturnValues($exceptions); } public function andThrows($exception, $message = '', $code = 0, ?\Exception $previous = null) { return $this->andThrow($exception, $message, $code, $previous); } /** * Sets up a closure that will yield each of the provided args * * @param mixed ...$args * * @return self */ public function andYield(...$args) { $closure = static function () use ($args) { foreach ($args as $arg) { yield $arg; } }; $this->_closureQueue = [$closure]; return $this; } /** * Sets next count validator to the AtLeast instance * * @return self */ public function atLeast() { $this->_countValidatorClass = AtLeast::class; return $this; } /** * Sets next count validator to the AtMost instance * * @return self */ public function atMost() { $this->_countValidatorClass = AtMost::class; return $this; } /** * Set the exception message * * @param string $message * * @return $this */ public function because($message) { $this->_because = $message; return $this; } /** * Shorthand for setting minimum and maximum constraints on call counts * * @param int $minimum * @param int $maximum */ public function between($minimum, $maximum) { return $this->atLeast()->times($minimum)->atMost()->times($maximum); } /** * Mark this expectation as being a default * * @return self */ public function byDefault() { $director = $this->_mock->mockery_getExpectationsFor($this->_name); if ($director instanceof ExpectationDirector) { $director->makeExpectationDefault($this); } return $this; } /** * @return null|string */ public function getExceptionMessage() { return $this->_because; } /** * Return the parent mock of the expectation * * @return LegacyMockInterface|MockInterface */ public function getMock() { return $this->_mock; } public function getName() { return $this->_name; } /** * Return order number * * @return int */ public function getOrderNumber() { return $this->_orderNumber; } /** * Indicates call order should apply globally * * @return self */ public function globally() { $this->_globally = true; return $this; } /** * Check if there is a constraint on call count * * @return bool */ public function isCallCountConstrained() { return $this->_countValidators !== []; } /** * Checks if this expectation is eligible for additional calls * * @return bool */ public function isEligible() { foreach ($this->_countValidators as $validator) { if (! $validator->isEligible($this->_actualCount)) { return false; } } return true; } /** * Check if passed arguments match an argument expectation * * @return bool */ public function matchArgs(array $args) { if ($this->isArgumentListMatcher()) { return $this->_matchArg($this->_expectedArgs[0], $args); } $argCount = count($args); $expectedArgsCount = count($this->_expectedArgs); if ($argCount === $expectedArgsCount) { return $this->_matchArgs($args); } $lastExpectedArgument = $this->_expectedArgs[$expectedArgsCount - 1]; if ($lastExpectedArgument instanceof AndAnyOtherArgs) { $firstCorrespondingKey = array_search($lastExpectedArgument, $this->_expectedArgs, true); $args = array_slice($args, 0, $firstCorrespondingKey); return $this->_matchArgs($args); } return false; } /** * Indicates that this expectation is never expected to be called * * @return self */ public function never() { return $this->times(0); } /** * Indicates that this expectation is expected exactly once * * @return self */ public function once() { return $this->times(1); } /** * Indicates that this expectation must be called in a specific given order * * @param string $group Name of the ordered group * * @return self */ public function ordered($group = null) { if ($this->_globally) { $this->_globalOrderNumber = $this->_defineOrdered($group, $this->_mock->mockery_getContainer()); } else { $this->_orderNumber = $this->_defineOrdered($group, $this->_mock); } $this->_globally = false; return $this; } /** * Flag this expectation as calling the original class method with * the provided arguments instead of using a return value queue. * * @return self */ public function passthru() { if ($this->_mock instanceof Mock) { throw new Exception( 'Mock Objects not created from a loaded/existing class are incapable of passing method calls through to a parent class' ); } $this->_passthru = true; return $this; } /** * Alias to andSet(). Allows the natural English construct * - set('foo', 'bar')->andReturn('bar') * * @param string $name * @param mixed $value * * @return self */ public function set($name, $value) { return $this->andSet(...func_get_args()); } /** * Indicates the number of times this expectation should occur * * @param int $limit * * @throws InvalidArgumentException * * @return self */ public function times($limit = null) { if ($limit === null) { return $this; } if (! is_int($limit)) { throw new InvalidArgumentException('The passed Times limit should be an integer value'); } if ($this->_expectedCount === 0) { @trigger_error(self::ERROR_ZERO_INVOCATION, E_USER_DEPRECATED); // throw new \InvalidArgumentException(self::ERROR_ZERO_INVOCATION); } if ($limit === 0) { $this->_countValidators = []; } $this->_expectedCount = $limit; $this->_countValidators[$this->_countValidatorClass] = new $this->_countValidatorClass($this, $limit); if ($this->_countValidatorClass !== Exact::class) { $this->_countValidatorClass = Exact::class; unset($this->_countValidators[$this->_countValidatorClass]); } return $this; } /** * Indicates that this expectation is expected exactly twice * * @return self */ public function twice() { return $this->times(2); } /** * Verify call order * * @return void */ public function validateOrder() { if ($this->_orderNumber) { $this->_mock->mockery_validateOrder((string) $this, $this->_orderNumber, $this->_mock); } if ($this->_globalOrderNumber) { $this->_mock->mockery_getContainer()->mockery_validateOrder( (string) $this, $this->_globalOrderNumber, $this->_mock ); } } /** * Verify this expectation * * @return void */ public function verify() { foreach ($this->_countValidators as $validator) { $validator->validate($this->_actualCount); } } /** * Verify the current call, i.e. that the given arguments match those * of this expectation * * @throws Throwable * * @return mixed */ public function verifyCall(array $args) { $this->validateOrder(); ++$this->_actualCount; if ($this->_passthru === true) { return $this->_mock->mockery_callSubjectMethod($this->_name, $args); } $return = $this->_getReturnValue($args); $this->throwAsNecessary($return); $this->_setValues(); return $return; } /** * Expected argument setter for the expectation * * @param mixed ...$args * * @return self */ public function with(...$args) { return $this->withArgs($args); } /** * Set expectation that any arguments are acceptable * * @return self */ public function withAnyArgs() { $this->_expectedArgs = [new AnyArgs()]; return $this; } /** * Expected arguments for the expectation passed as an array or a closure that matches each passed argument on * each function call. * * @param array|Closure $argsOrClosure * * @return self */ public function withArgs($argsOrClosure) { if (is_array($argsOrClosure)) { return $this->withArgsInArray($argsOrClosure); } if ($argsOrClosure instanceof Closure) { return $this->withArgsMatchedByClosure($argsOrClosure); } throw new InvalidArgumentException(sprintf( 'Call to %s with an invalid argument (%s), only array and closure are allowed', __METHOD__, $argsOrClosure )); } /** * Set with() as no arguments expected * * @return self */ public function withNoArgs() { $this->_expectedArgs = [new NoArgs()]; return $this; } /** * Expected arguments should partially match the real arguments * * @param mixed ...$expectedArgs * * @return self */ public function withSomeOfArgs(...$expectedArgs) { return $this->withArgs(static function (...$args) use ($expectedArgs): bool { foreach ($expectedArgs as $expectedArg) { if (! in_array($expectedArg, $args, true)) { return false; } } return true; }); } /** * Indicates this expectation should occur zero or more times * * @return self */ public function zeroOrMoreTimes() { return $this->atLeast()->never(); } /** * Setup the ordering tracking on the mock or mock container * * @param string $group * @param object $ordering * * @return int */ protected function _defineOrdered($group, $ordering) { $groups = $ordering->mockery_getGroups(); if ($group === null) { return $ordering->mockery_allocateOrder(); } if (array_key_exists($group, $groups)) { return $groups[$group]; } $result = $ordering->mockery_allocateOrder(); $ordering->mockery_setGroup($group, $result); return $result; } /** * Fetch the return value for the matching args * * @return mixed */ protected function _getReturnValue(array $args) { $closureQueueCount = count($this->_closureQueue); if ($closureQueueCount > 1) { return array_shift($this->_closureQueue)(...$args); } if ($closureQueueCount > 0) { return current($this->_closureQueue)(...$args); } $returnQueueCount = count($this->_returnQueue); if ($returnQueueCount > 1) { return array_shift($this->_returnQueue); } if ($returnQueueCount > 0) { return current($this->_returnQueue); } return $this->_mock->mockery_returnValueForMethod($this->_name); } /** * Check if passed argument matches an argument expectation * * @param mixed $expected * @param mixed $actual * * @return bool */ protected function _matchArg($expected, &$actual) { if ($expected === $actual) { return true; } if ($expected instanceof MatcherInterface) { return $expected->match($actual); } if ($expected instanceof Constraint) { return (bool) $expected->evaluate($actual, '', true); } if ($expected instanceof Matcher || $expected instanceof Hamcrest_Matcher) { @trigger_error('Hamcrest package has been deprecated and will be removed in 2.0', E_USER_DEPRECATED); return $expected->matches($actual); } if (is_object($expected)) { $matcher = Mockery::getConfiguration()->getDefaultMatcher(get_class($expected)); return $matcher === null ? false : $this->_matchArg(new $matcher($expected), $actual); } if (is_object($actual) && is_string($expected) && $actual instanceof $expected) { return true; } return $expected == $actual; } /** * Check if the passed arguments match the expectations, one by one. * * @param array $args * * @return bool */ protected function _matchArgs($args) { for ($index = 0, $argCount = count($args); $index < $argCount; ++$index) { $param = &$args[$index]; if (! $this->_matchArg($this->_expectedArgs[$index], $param)) { return false; } } return true; } /** * Sets public properties with queued values to the mock object * * @return void */ protected function _setValues() { $mockClass = get_class($this->_mock); $container = $this->_mock->mockery_getContainer(); $mocks = $container->getMocks(); foreach ($this->_setQueue as $name => &$values) { if ($values === []) { continue; } $value = array_shift($values); $this->_mock->{$name} = $value; foreach ($mocks as $mock) { if (! $mock instanceof $mockClass) { continue; } if (! $mock->mockery_isInstance()) { continue; } $mock->{$name} = $value; } } } /** * @template TExpectedArg * * @param TExpectedArg $expectedArg * * @return bool */ private function isAndAnyOtherArgumentsMatcher($expectedArg) { return $expectedArg instanceof AndAnyOtherArgs; } /** * Check if the registered expectation is an ArgumentListMatcher * * @return bool */ private function isArgumentListMatcher() { return $this->_expectedArgs !== [] && $this->_expectedArgs[0] instanceof ArgumentListMatcher; } /** * Throws an exception if the expectation has been configured to do so * * @param Throwable $return * * @throws Throwable * * @return void */ private function throwAsNecessary($return) { if (! $this->_throw) { return; } if (! $return instanceof Throwable) { return; } throw $return; } /** * Expected arguments for the expectation passed as an array * * @return self */ private function withArgsInArray(array $arguments) { if ($arguments === []) { return $this->withNoArgs(); } $this->_expectedArgs = $arguments; return $this; } /** * Expected arguments have to be matched by the given closure. * * @return self */ private function withArgsMatchedByClosure(Closure $closure) { $this->_expectedArgs = [new MultiArgumentClosure($closure)]; return $this; } } PKZ.j<<0mockery/library/Mockery/VerificationDirector.phpnuW+AreceivedMethodCalls = $receivedMethodCalls; $this->expectation = $expectation; } /** * @return self */ public function atLeast() { return $this->cloneWithoutCountValidatorsApplyAndVerify('atLeast', []); } /** * @return self */ public function atMost() { return $this->cloneWithoutCountValidatorsApplyAndVerify('atMost', []); } /** * @param int $minimum * @param int $maximum * * @return self */ public function between($minimum, $maximum) { return $this->cloneWithoutCountValidatorsApplyAndVerify('between', [$minimum, $maximum]); } /** * @return self */ public function once() { return $this->cloneWithoutCountValidatorsApplyAndVerify('once', []); } /** * @param int $limit * * @return self */ public function times($limit = null) { return $this->cloneWithoutCountValidatorsApplyAndVerify('times', [$limit]); } /** * @return self */ public function twice() { return $this->cloneWithoutCountValidatorsApplyAndVerify('twice', []); } public function verify() { $this->receivedMethodCalls->verify($this->expectation); } /** * @template TArgs * * @param TArgs $args * * @return self */ public function with(...$args) { return $this->cloneApplyAndVerify('with', $args); } /** * @return self */ public function withAnyArgs() { return $this->cloneApplyAndVerify('withAnyArgs', []); } /** * @template TArgs * * @param TArgs $args * * @return self */ public function withArgs($args) { return $this->cloneApplyAndVerify('withArgs', [$args]); } /** * @return self */ public function withNoArgs() { return $this->cloneApplyAndVerify('withNoArgs', []); } /** * @param string $method * @param array $args * * @return self */ protected function cloneApplyAndVerify($method, $args) { $verificationExpectation = clone $this->expectation; $verificationExpectation->{$method}(...$args); $verificationDirector = new self($this->receivedMethodCalls, $verificationExpectation); $verificationDirector->verify(); return $verificationDirector; } /** * @param string $method * @param array $args * * @return self */ protected function cloneWithoutCountValidatorsApplyAndVerify($method, $args) { $verificationExpectation = clone $this->expectation; $verificationExpectation->clearCountValidators(); $verificationExpectation->{$method}(...$args); $verificationDirector = new self($this->receivedMethodCalls, $verificationExpectation); $verificationDirector->verify(); return $verificationDirector; } } PKZ:kiHiH%mockery/library/Mockery/Container.phpnuW+A */ protected $_groups = []; /** * @var LoaderInterface */ protected $_loader; /** * Store of mock objects * * @var array|array-key,LegacyMockInterface&MockInterface&TMockObject> */ protected $_mocks = []; /** * @var array */ protected $_namedMocks = []; /** * @var Instantiator */ protected $instantiator; public function __construct(?Generator $generator = null, ?LoaderInterface $loader = null, ?Instantiator $instantiator = null) { $this->_generator = $generator instanceof Generator ? $generator : Mockery::getDefaultGenerator(); $this->_loader = $loader instanceof LoaderInterface ? $loader : Mockery::getDefaultLoader(); $this->instantiator = $instantiator instanceof Instantiator ? $instantiator : new Instantiator(); } /** * Return a specific remembered mock according to the array index it * was stored to in this container instance * * @template TMock of object * * @param class-string $reference * * @return null|(LegacyMockInterface&MockInterface&TMock) */ public function fetchMock($reference) { return $this->_mocks[$reference] ?? null; } /** * @return Generator */ public function getGenerator() { return $this->_generator; } /** * @param string $method * @param string $parent * * @return null|string */ public function getKeyOfDemeterMockFor($method, $parent) { $keys = array_keys($this->_mocks); $match = preg_grep('/__demeter_' . md5($parent) . sprintf('_%s$/', $method), $keys); if ($match === false) { return null; } if ($match === []) { return null; } return array_values($match)[0]; } /** * @return LoaderInterface */ public function getLoader() { return $this->_loader; } /** * @template TMock of object * @return array|array-key,LegacyMockInterface&MockInterface&TMockObject> */ public function getMocks() { return $this->_mocks; } /** * @return void */ public function instanceMock() { } /** * see http://php.net/manual/en/language.oop5.basic.php * * @param string $className * * @return bool */ public function isValidClassName($className) { if ($className[0] === '\\') { $className = substr($className, 1); // remove the first backslash } // all the namespaces and class name should match the regex return array_filter( explode('\\', $className), static function ($name): bool { return ! preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $name); } ) === []; } /** * Generates a new mock object for this container * * I apologies in advance for this. A God Method just fits the API which * doesn't require differentiating between classes, interfaces, abstracts, * names or partials - just so long as it's something that can be mocked. * I'll refactor it one day so it's easier to follow. * * @template TMock of object * * @param array|TMock|Closure(LegacyMockInterface&MockInterface&TMock):LegacyMockInterface&MockInterface&TMock|array> $args * * @throws ReflectionException|RuntimeException * * @return LegacyMockInterface&MockInterface&TMock */ public function mock(...$args) { /** @var null|MockConfigurationBuilder $builder */ $builder = null; /** @var null|callable $expectationClosure */ $expectationClosure = null; $partialMethods = null; $quickDefinitions = []; $constructorArgs = null; $blocks = []; if (count($args) > 1) { $finalArg = array_pop($args); if (is_callable($finalArg) && is_object($finalArg)) { $expectationClosure = $finalArg; } else { $args[] = $finalArg; } } foreach ($args as $k => $arg) { if ($arg instanceof MockConfigurationBuilder) { $builder = $arg; unset($args[$k]); } } reset($args); $builder = $builder ?? new MockConfigurationBuilder(); $mockeryConfiguration = Mockery::getConfiguration(); $builder->setParameterOverrides($mockeryConfiguration->getInternalClassMethodParamMaps()); $builder->setConstantsMap($mockeryConfiguration->getConstantsMap()); while ($args !== []) { $arg = array_shift($args); // check for multiple interfaces if (is_string($arg)) { foreach (explode('|', $arg) as $type) { if ($arg === 'null') { // skip PHP 8 'null's continue; } if (strpos($type, ',') && !strpos($type, ']')) { $interfaces = explode(',', str_replace(' ', '', $type)); $builder->addTargets($interfaces); continue; } if (strpos($type, 'alias:') === 0) { $type = str_replace('alias:', '', $type); $builder->addTarget('stdClass'); $builder->setName($type); continue; } if (strpos($type, 'overload:') === 0) { $type = str_replace('overload:', '', $type); $builder->setInstanceMock(true); $builder->addTarget('stdClass'); $builder->setName($type); continue; } if ($type[strlen($type) - 1] === ']') { $parts = explode('[', $type); $class = $parts[0]; if (! class_exists($class, true) && ! interface_exists($class, true)) { throw new Exception('Can only create a partial mock from an existing class or interface'); } $builder->addTarget($class); $partialMethods = array_filter( explode(',', strtolower(rtrim(str_replace(' ', '', $parts[1]), ']'))) ); foreach ($partialMethods as $partialMethod) { if ($partialMethod[0] === '!') { $builder->addBlackListedMethod(substr($partialMethod, 1)); continue; } $builder->addWhiteListedMethod($partialMethod); } continue; } if (class_exists($type, true) || interface_exists($type, true) || trait_exists($type, true)) { $builder->addTarget($type); continue; } if (! $mockeryConfiguration->mockingNonExistentMethodsAllowed()) { throw new Exception(sprintf("Mockery can't find '%s' so can't mock it", $type)); } if (! $this->isValidClassName($type)) { throw new Exception('Class name contains invalid characters'); } $builder->addTarget($type); // unions are "sum" types and not "intersections", and so we must only process the first part break; } continue; } if (is_object($arg)) { $builder->addTarget($arg); continue; } if (is_array($arg)) { if ([] !== $arg && array_keys($arg) !== range(0, count($arg) - 1)) { // if associative array if (array_key_exists(self::BLOCKS, $arg)) { $blocks = $arg[self::BLOCKS]; } unset($arg[self::BLOCKS]); $quickDefinitions = $arg; continue; } $constructorArgs = $arg; continue; } throw new Exception(sprintf( 'Unable to parse arguments sent to %s::mock()', get_class($this) )); } $builder->addBlackListedMethods($blocks); if ($constructorArgs !== null) { $builder->addBlackListedMethod('__construct'); // we need to pass through } else { $builder->setMockOriginalDestructor(true); } if ($partialMethods !== null && $constructorArgs === null) { $constructorArgs = []; } $config = $builder->getMockConfiguration(); $this->checkForNamedMockClashes($config); $def = $this->getGenerator()->generate($config); $className = $def->getClassName(); if (class_exists($className, $attemptAutoload = false)) { $rfc = new ReflectionClass($className); if (! $rfc->implementsInterface(LegacyMockInterface::class)) { throw new RuntimeException(sprintf('Could not load mock %s, class already exists', $className)); } } $this->getLoader()->load($def); $mock = $this->_getInstance($className, $constructorArgs); $mock->mockery_init($this, $config->getTargetObject(), $config->isInstanceMock()); if ($quickDefinitions !== []) { if ($mockeryConfiguration->getQuickDefinitions()->shouldBeCalledAtLeastOnce()) { $mock->shouldReceive($quickDefinitions)->atLeast()->once(); } else { $mock->shouldReceive($quickDefinitions)->byDefault(); } } // if the last parameter passed to mock() is a closure, if ($expectationClosure instanceof Closure) { // call the closure with the mock object $expectationClosure($mock); } return $this->rememberMock($mock); } /** * Fetch the next available allocation order number * * @return int */ public function mockery_allocateOrder() { return ++$this->_allocatedOrder; } /** * Reset the container to its original state * * @return void */ public function mockery_close() { foreach ($this->_mocks as $mock) { $mock->mockery_teardown(); } $this->_mocks = []; } /** * Get current ordered number * * @return int */ public function mockery_getCurrentOrder() { return $this->_currentOrder; } /** * Gets the count of expectations on the mocks * * @return int */ public function mockery_getExpectationCount() { $count = 0; foreach ($this->_mocks as $mock) { $count += $mock->mockery_getExpectationCount(); } return $count; } /** * Fetch array of ordered groups * * @return array */ public function mockery_getGroups() { return $this->_groups; } /** * Set current ordered number * * @param int $order * * @return int The current order number that was set */ public function mockery_setCurrentOrder($order) { return $this->_currentOrder = $order; } /** * Set ordering for a group * * @param string $group * @param int $order * * @return void */ public function mockery_setGroup($group, $order) { $this->_groups[$group] = $order; } /** * Tear down tasks for this container * * @throws PHPException */ public function mockery_teardown() { try { $this->mockery_verify(); } catch (PHPException $phpException) { $this->mockery_close(); throw $phpException; } } /** * Retrieves all exceptions thrown by mocks * * @return array */ public function mockery_thrownExceptions() { /** @var array $exceptions */ $exceptions = []; foreach ($this->_mocks as $mock) { foreach ($mock->mockery_thrownExceptions() as $exception) { $exceptions[] = $exception; } } return $exceptions; } /** * Validate the current mock's ordering * * @param string $method * @param int $order * * @throws Exception */ public function mockery_validateOrder($method, $order, LegacyMockInterface $mock) { if ($order < $this->_currentOrder) { $exception = new InvalidOrderException( sprintf( 'Method %s called out of order: expected order %d, was %d', $method, $order, $this->_currentOrder ) ); $exception->setMock($mock) ->setMethodName($method) ->setExpectedOrder($order) ->setActualOrder($this->_currentOrder); throw $exception; } $this->mockery_setCurrentOrder($order); } /** * Verify the container mocks */ public function mockery_verify() { foreach ($this->_mocks as $mock) { $mock->mockery_verify(); } } /** * Store a mock and set its container reference * * @template TRememberMock of object * * @param LegacyMockInterface&MockInterface&TRememberMock $mock * * @return LegacyMockInterface&MockInterface&TRememberMock */ public function rememberMock(LegacyMockInterface $mock) { $class = get_class($mock); if (! array_key_exists($class, $this->_mocks)) { return $this->_mocks[$class] = $mock; } /** * This condition triggers for an instance mock where origin mock * is already remembered */ return $this->_mocks[] = $mock; } /** * Retrieve the last remembered mock object, * which is the same as saying retrieve the current mock being programmed where you have yet to call mock() * to change it thus why the method name is "self" since it will be used during the programming of the same mock. * * @return LegacyMockInterface|MockInterface */ public function self() { $mocks = array_values($this->_mocks); $index = count($mocks) - 1; return $mocks[$index]; } /** * @template TMock of object * @template TMixed * * @param class-string $mockName * @param null|array $constructorArgs * * @return TMock */ protected function _getInstance($mockName, $constructorArgs = null) { if ($constructorArgs !== null) { return (new ReflectionClass($mockName))->newInstanceArgs($constructorArgs); } try { $instance = $this->instantiator->instantiate($mockName); } catch (PHPException $phpException) { /** @var class-string $internalMockName */ $internalMockName = $mockName . '_Internal'; if (! class_exists($internalMockName)) { eval(sprintf( 'class %s extends %s { public function __construct() {} }', $internalMockName, $mockName )); } $instance = new $internalMockName(); } return $instance; } protected function checkForNamedMockClashes($config) { $name = $config->getName(); if ($name === null) { return; } $hash = $config->getHash(); if (array_key_exists($name, $this->_namedMocks) && $hash !== $this->_namedMocks[$name]) { throw new Exception( sprintf("The mock named '%s' has been already defined with a different mock configuration", $name) ); } $this->_namedMocks[$name] = $hash; } } PKZ%mockery/library/Mockery/Exception.phpnuW+Aclosure = $closure; } /** * @return mixed */ public function __invoke() { return ($this->closure)(...func_get_args()); } } PKZ 6 0mockery/library/Mockery/CompositeExpectation.phpnuW+A */ protected $_expectations = []; /** * Intercept any expectation calls and direct against all expectations * * @param string $method * * @return self */ public function __call($method, array $args) { foreach ($this->_expectations as $expectation) { $expectation->{$method}(...$args); } return $this; } /** * Return the string summary of this composite expectation * * @return string */ public function __toString() { $parts = array_map(static function (ExpectationInterface $expectation): string { return (string) $expectation; }, $this->_expectations); return '[' . implode(', ', $parts) . ']'; } /** * Add an expectation to the composite * * @param ExpectationInterface|HigherOrderMessage $expectation * * @return void */ public function add($expectation) { $this->_expectations[] = $expectation; } /** * @param mixed ...$args */ public function andReturn(...$args) { return $this->__call(__FUNCTION__, $args); } /** * Set a return value, or sequential queue of return values * * @param mixed ...$args * * @return self */ public function andReturns(...$args) { return $this->andReturn(...$args); } /** * Return the parent mock of the first expectation * * @return LegacyMockInterface&MockInterface */ public function getMock() { reset($this->_expectations); $first = current($this->_expectations); return $first->getMock(); } /** * Return order number of the first expectation * * @return int */ public function getOrderNumber() { reset($this->_expectations); $first = current($this->_expectations); return $first->getOrderNumber(); } /** * Mockery API alias to getMock * * @return LegacyMockInterface&MockInterface */ public function mock() { return $this->getMock(); } /** * Starts a new expectation addition on the first mock which is the primary target outside of a demeter chain * * @param mixed ...$args * * @return Expectation */ public function shouldNotReceive(...$args) { reset($this->_expectations); $first = current($this->_expectations); return $first->getMock()->shouldNotReceive(...$args); } /** * Starts a new expectation addition on the first mock which is the primary target, outside of a demeter chain * * @param mixed ...$args * * @return Expectation */ public function shouldReceive(...$args) { reset($this->_expectations); $first = current($this->_expectations); return $first->getMock()->shouldReceive(...$args); } } PKZ^}^^/mockery/library/Mockery/LegacyMockInterface.phpnuW+A $args * * @return null|Expectation */ public function mockery_findExpectation($method, array $args); /** * Return the container for this mock * * @return Container */ public function mockery_getContainer(); /** * Get current ordered number * * @return int */ public function mockery_getCurrentOrder(); /** * Gets the count of expectations for this mock * * @return int */ public function mockery_getExpectationCount(); /** * Return the expectations director for the given method * * @param string $method * * @return null|ExpectationDirector */ public function mockery_getExpectationsFor($method); /** * Fetch array of ordered groups * * @return array */ public function mockery_getGroups(); /** * @return string[] */ public function mockery_getMockableMethods(); /** * @return array */ public function mockery_getMockableProperties(); /** * Return the name for this mock * * @return string */ public function mockery_getName(); /** * Alternative setup method to constructor * * @param object $partialObject * * @return void */ public function mockery_init(?Container $container = null, $partialObject = null); /** * @return bool */ public function mockery_isAnonymous(); /** * Set current ordered number * * @param int $order * * @return int */ public function mockery_setCurrentOrder($order); /** * Return the expectations director for the given method * * @param string $method * * @return null|ExpectationDirector */ public function mockery_setExpectationsFor($method, ExpectationDirector $director); /** * Set ordering for a group * * @param string $group * @param int $order * * @return void */ public function mockery_setGroup($group, $order); /** * Tear down tasks for this mock * * @return void */ public function mockery_teardown(); /** * Validate the current mock's ordering * * @param string $method * @param int $order * * @throws Exception * * @return void */ public function mockery_validateOrder($method, $order); /** * Iterate across all expectation directors and validate each * * @throws Throwable * * @return void */ public function mockery_verify(); /** * Allows additional methods to be mocked that do not explicitly exist on mocked class * * @param string $method the method name to be mocked * @return self */ public function shouldAllowMockingMethod($method); /** * @return self */ public function shouldAllowMockingProtectedMethods(); /** * Set mock to defer unexpected methods to its parent if possible * * @deprecated since 1.4.0. Please use makePartial() instead. * * @return self */ public function shouldDeferMissing(); /** * @return self */ public function shouldHaveBeenCalled(); /** * @template TMixed * @param string $method * @param null|array|Closure $args * * @return self */ public function shouldHaveReceived($method, $args = null); /** * Set mock to ignore unexpected methods and return Undefined class * * @template TReturnValue * * @param null|TReturnValue $returnValue the default return value for calls to missing functions on this mock * * @return self */ public function shouldIgnoreMissing($returnValue = null); /** * @template TMixed * @param null|array $args (optional) * * @return self */ public function shouldNotHaveBeenCalled(?array $args = null); /** * @template TMixed * @param string $method * @param null|array|Closure $args * * @return self */ public function shouldNotHaveReceived($method, $args = null); /** * Shortcut method for setting an expectation that a method should not be called. * * @param string ...$methodNames one or many methods that are expected not to be called in this mock * * @return Expectation|ExpectationInterface|HigherOrderMessage */ public function shouldNotReceive(...$methodNames); /** * Set expected method calls * * @param string ...$methodNames one or many methods that are expected to be called in this mock * * @return Expectation|ExpectationInterface|HigherOrderMessage */ public function shouldReceive(...$methodNames); } PKZs:ZZ3mockery/library/Mockery/VerificationExpectation.phpnuW+A_actualCount = 0; } /** * @return void */ public function clearCountValidators() { $this->_countValidators = []; } } PKZ!q**/mockery/library/Mockery/ExpectationDirector.phpnuW+A */ protected $_defaults = []; /** * Stores an array of all expectations for this mock * * @var list */ protected $_expectations = []; /** * The expected order of next call * * @var int */ protected $_expectedOrder = null; /** * Mock object the director is attached to * * @var LegacyMockInterface|MockInterface */ protected $_mock = null; /** * Method name the director is directing * * @var string */ protected $_name = null; /** * Constructor * * @param string $name */ public function __construct($name, LegacyMockInterface $mock) { $this->_name = $name; $this->_mock = $mock; } /** * Add a new expectation to the director */ public function addExpectation(Expectation $expectation) { $this->_expectations[] = $expectation; } /** * Handle a method call being directed by this instance * * @return mixed */ public function call(array $args) { $expectation = $this->findExpectation($args); if ($expectation !== null) { return $expectation->verifyCall($args); } $exception = new NoMatchingExpectationException( 'No matching handler found for ' . $this->_mock->mockery_getName() . '::' . Mockery::formatArgs($this->_name, $args) . '. Either the method was unexpected or its arguments matched' . ' no expected argument list for this method' . PHP_EOL . PHP_EOL . Mockery::formatObjects($args) ); $exception->setMock($this->_mock) ->setMethodName($this->_name) ->setActualArguments($args); throw $exception; } /** * Attempt to locate an expectation matching the provided args * * @return mixed */ public function findExpectation(array $args) { $expectation = null; if ($this->_expectations !== []) { $expectation = $this->_findExpectationIn($this->_expectations, $args); } if ($expectation === null && $this->_defaults !== []) { return $this->_findExpectationIn($this->_defaults, $args); } return $expectation; } /** * Return all expectations assigned to this director * * @return array */ public function getDefaultExpectations() { return $this->_defaults; } /** * Return the number of expectations assigned to this director. * * @return int */ public function getExpectationCount() { $count = 0; $expectations = $this->getExpectations(); if ($expectations === []) { $expectations = $this->getDefaultExpectations(); } foreach ($expectations as $expectation) { if ($expectation->isCallCountConstrained()) { ++$count; } } return $count; } /** * Return all expectations assigned to this director * * @return array */ public function getExpectations() { return $this->_expectations; } /** * Make the given expectation a default for all others assuming it was correctly created last * * @throws Exception * * @return void */ public function makeExpectationDefault(Expectation $expectation) { if (end($this->_expectations) === $expectation) { array_pop($this->_expectations); array_unshift($this->_defaults, $expectation); return; } throw new Exception('Cannot turn a previously defined expectation into a default'); } /** * Verify all expectations of the director * * @throws Exception * * @return void */ public function verify() { if ($this->_expectations !== []) { foreach ($this->_expectations as $expectation) { $expectation->verify(); } return; } foreach ($this->_defaults as $expectation) { $expectation->verify(); } } /** * Search current array of expectations for a match * * @param array $expectations * * @return null|ExpectationInterface */ protected function _findExpectationIn(array $expectations, array $args) { foreach ($expectations as $expectation) { if (! $expectation->isEligible()) { continue; } if (! $expectation->matchArgs($args)) { continue; } return $expectation; } foreach ($expectations as $expectation) { if ($expectation->matchArgs($args)) { return $expectation; } } return null; } } PKZbepp:mockery/library/Mockery/Generator/TargetClassInterface.phpnuW+A */ public function getAttributes(); /** * Returns the targetClass's interfaces. * * @return array */ public function getInterfaces(); /** * Returns the targetClass's methods. * * @return array */ public function getMethods(); /** * Returns the targetClass's name. * * @return class-string */ public function getName(); /** * Returns the targetClass's namespace name. * * @return string */ public function getNamespaceName(); /** * Returns the targetClass's short name. * * @return string */ public function getShortName(); /** * Returns whether the targetClass has * an internal ancestor. * * @return bool */ public function hasInternalAncestor(); /** * Returns whether the targetClass is in * the passed interface. * * @param class-string|string $interface * * @return bool */ public function implementsInterface($interface); /** * Returns whether the targetClass is in namespace. * * @return bool */ public function inNamespace(); /** * Returns whether the targetClass is abstract. * * @return bool */ public function isAbstract(); /** * Returns whether the targetClass is final. * * @return bool */ public function isFinal(); } PKZJ9 9 Amockery/library/Mockery/Generator/StringManipulationGenerator.phpnuW+A */ protected $passes = []; /** * @var string */ private $code; /** * @param list $passes */ public function __construct(array $passes) { $this->passes = $passes; $this->code = file_get_contents(__DIR__ . '/../Mock.php'); } /** * @param Pass $pass * @return void */ public function addPass(Pass $pass) { $this->passes[] = $pass; } /** * @return MockDefinition */ public function generate(MockConfiguration $config) { $className = $config->getName() ?: $config->generateName(); $namedConfig = $config->rename($className); $code = $this->code; foreach ($this->passes as $pass) { $code = $pass->apply($code, $namedConfig); } return new MockDefinition($namedConfig, $code); } /** * Creates a new StringManipulationGenerator with the default passes * * @return StringManipulationGenerator */ public static function withDefaultPasses() { return new static([ new CallTypeHintPass(), new MagicMethodTypeHintsPass(), new ClassPass(), new TraitPass(), new ClassNamePass(), new InstanceMockPass(), new InterfacePass(), new AvoidMethodClashPass(), new MethodDefinitionPass(), new RemoveUnserializeForInternalSerializableClassesPass(), new RemoveBuiltinMethodsThatAreFinalPass(), new RemoveDestructorPass(), new ConstantsPass(), new ClassAttributesPass(), ]); } } PKZk>mockery/library/Mockery/Generator/MockConfigurationBuilder.phpnuW+A */ protected $blackListedMethods = [ '__call', '__callStatic', '__clone', '__wakeup', '__set', '__get', '__toString', '__isset', '__destruct', '__debugInfo', ## mocking this makes it difficult to debug with xdebug // below are reserved words in PHP '__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor', ]; /** * @var array */ protected $constantsMap = []; /** * @var bool */ protected $instanceMock = false; /** * @var bool */ protected $mockOriginalDestructor = false; /** * @var string */ protected $name; /** * @var array */ protected $parameterOverrides = []; /** * @var list */ protected $php7SemiReservedKeywords = [ 'callable', 'class', 'trait', 'extends', 'implements', 'static', 'abstract', 'final', 'public', 'protected', 'private', 'const', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endwhile', 'and', 'global', 'goto', 'instanceof', 'insteadof', 'interface', 'namespace', 'new', 'or', 'xor', 'try', 'use', 'var', 'exit', 'list', 'clone', 'include', 'include_once', 'throw', 'array', 'print', 'echo', 'require', 'require_once', 'return', 'else', 'elseif', 'default', 'break', 'continue', 'switch', 'yield', 'function', 'if', 'endswitch', 'finally', 'for', 'foreach', 'declare', 'case', 'do', 'while', 'as', 'catch', 'die', 'self', 'parent', ]; /** * @var array */ protected $targets = []; /** * @var array */ protected $whiteListedMethods = []; public function __construct() { $this->blackListedMethods = array_diff($this->blackListedMethods, $this->php7SemiReservedKeywords); } /** * @param string $blackListedMethod * @return self */ public function addBlackListedMethod($blackListedMethod) { $this->blackListedMethods[] = $blackListedMethod; return $this; } /** * @param list $blackListedMethods * @return self */ public function addBlackListedMethods(array $blackListedMethods) { foreach ($blackListedMethods as $method) { $this->addBlackListedMethod($method); } return $this; } /** * @param class-string $target * @return self */ public function addTarget($target) { $this->targets[] = $target; return $this; } /** * @param list $targets * @return self */ public function addTargets($targets) { foreach ($targets as $target) { $this->addTarget($target); } return $this; } /** * @return self */ public function addWhiteListedMethod($whiteListedMethod) { $this->whiteListedMethods[] = $whiteListedMethod; return $this; } /** * @return self */ public function addWhiteListedMethods(array $whiteListedMethods) { foreach ($whiteListedMethods as $method) { $this->addWhiteListedMethod($method); } return $this; } /** * @return MockConfiguration */ public function getMockConfiguration() { return new MockConfiguration( $this->targets, $this->blackListedMethods, $this->whiteListedMethods, $this->name, $this->instanceMock, $this->parameterOverrides, $this->mockOriginalDestructor, $this->constantsMap ); } /** * @param list $blackListedMethods * @return self */ public function setBlackListedMethods(array $blackListedMethods) { $this->blackListedMethods = $blackListedMethods; return $this; } /** * @return self */ public function setConstantsMap(array $map) { $this->constantsMap = $map; return $this; } /** * @param bool $instanceMock */ public function setInstanceMock($instanceMock) { $this->instanceMock = (bool) $instanceMock; return $this; } /** * @param bool $mockDestructor */ public function setMockOriginalDestructor($mockDestructor) { $this->mockOriginalDestructor = (bool) $mockDestructor; return $this; } /** * @param string $name */ public function setName($name) { $this->name = $name; return $this; } /** * @return self */ public function setParameterOverrides(array $overrides) { $this->parameterOverrides = $overrides; return $this; } /** * @param list $whiteListedMethods * @return self */ public function setWhiteListedMethods(array $whiteListedMethods) { $this->whiteListedMethods = $whiteListedMethods; return $this; } } PKZPJ}Nmockery/library/Mockery/Generator/StringManipulation/Pass/CallTypeHintPass.phpnuW+ArequiresCallTypeHintRemoval()) { $code = str_replace( 'public function __call($method, array $args)', 'public function __call($method, $args)', $code ); } if ($config->requiresCallStaticTypeHintRemoval()) { return str_replace( 'public static function __callStatic($method, array $args)', 'public static function __callStatic($method, $args)', $code ); } return $code; } } PKZ4qmockery/library/Mockery/Generator/StringManipulation/Pass/RemoveUnserializeForInternalSerializableClassesPass.phpnuW+AgetTargetClass(); if (! $target) { return $code; } if (! $target->hasInternalAncestor() || ! $target->implementsInterface('Serializable')) { return $code; } return $this->appendToClass( $code, PHP_VERSION_ID < 80100 ? self::DUMMY_METHOD_DEFINITION_LEGACY : self::DUMMY_METHOD_DEFINITION ); } protected function appendToClass($class, $code) { $lastBrace = strrpos($class, '}'); return substr($class, 0, $lastBrace) . $code . "\n }\n"; } } PKZZGmockery/library/Mockery/Generator/StringManipulation/Pass/ClassPass.phpnuW+AgetTargetClass(); if (! $target) { return $code; } if ($target->isFinal()) { return $code; } $className = ltrim($target->getName(), '\\'); if (! class_exists($className)) { Mockery::declareClass($className); } return str_replace( 'implements MockInterface', 'extends \\' . $className . ' implements MockInterface', $code ); } } PKZ]Qmockery/library/Mockery/Generator/StringManipulation/Pass/ClassAttributesPass.phpnuW+AgetTargetClass(); if (! $class) { return $code; } /** @var array $attributes */ $attributes = $class->getAttributes(); if ($attributes !== []) { return str_replace('#[\AllowDynamicProperties]', '#[' . implode(',', $attributes) . ']', $code); } return $code; } } PKZ_ZƗ Nmockery/library/Mockery/Generator/StringManipulation/Pass/InstanceMockPass.phpnuW+A_mockery_ignoreVerification = false; \$associatedRealObject = \Mockery::fetchMock(__CLASS__); foreach (get_object_vars(\$this) as \$attr => \$val) { if (\$attr !== "_mockery_ignoreVerification" && \$attr !== "_mockery_expectations") { \$this->\$attr = \$associatedRealObject->\$attr; } } \$directors = \$associatedRealObject->mockery_getExpectations(); foreach (\$directors as \$method=>\$director) { // get the director method needed \$existingDirector = \$this->mockery_getExpectationsFor(\$method); if (!\$existingDirector) { \$existingDirector = new \Mockery\ExpectationDirector(\$method, \$this); \$this->mockery_setExpectationsFor(\$method, \$existingDirector); } \$expectations = \$director->getExpectations(); foreach (\$expectations as \$expectation) { \$clonedExpectation = clone \$expectation; \$existingDirector->addExpectation(\$clonedExpectation); } \$defaultExpectations = \$director->getDefaultExpectations(); foreach (array_reverse(\$defaultExpectations) as \$expectation) { \$clonedExpectation = clone \$expectation; \$existingDirector->addExpectation(\$clonedExpectation); \$existingDirector->makeExpectationDefault(\$clonedExpectation); } } \Mockery::getContainer()->rememberMock(\$this); \$this->_mockery_constructorCalled(func_get_args()); } MOCK; /** * @param string $code * @return string */ public function apply($code, MockConfiguration $config) { if ($config->isInstanceMock()) { return $this->appendToClass($code, static::INSTANCE_MOCK_CODE); } return $code; } protected function appendToClass($class, $code) { $lastBrace = strrpos($class, '}'); return substr($class, 0, $lastBrace) . $code . "\n }\n"; } } PKZșbbVmockery/library/Mockery/Generator/StringManipulation/Pass/MagicMethodTypeHintsPass.phpnuW+AgetMagicMethods($config->getTargetClass()); foreach ($config->getTargetInterfaces() as $interface) { $magicMethods = array_merge($magicMethods, $this->getMagicMethods($interface)); } foreach ($magicMethods as $method) { $code = $this->applyMagicTypeHints($code, $method); } return $code; } /** * Returns the magic methods within the * passed DefinedTargetClass. * * @return array */ public function getMagicMethods(?TargetClassInterface $class = null) { if (! $class instanceof TargetClassInterface) { return []; } return array_filter($class->getMethods(), function (Method $method) { return in_array($method->getName(), $this->mockMagicMethods, true); }); } protected function renderTypeHint(Parameter $param) { $typeHint = $param->getTypeHint(); return $typeHint === null ? '' : sprintf('%s ', $typeHint); } /** * Applies type hints of magic methods from * class to the passed code. * * @param int $code * * @return string */ private function applyMagicTypeHints($code, Method $method) { if ($this->isMethodWithinCode($code, $method)) { $namedParameters = $this->getOriginalParameters($code, $method); $code = preg_replace( $this->getDeclarationRegex($method->getName()), $this->getMethodDeclaration($method, $namedParameters), $code ); } return $code; } /** * Returns a regex string used to match the * declaration of some method. * * @param string $methodName * * @return string */ private function getDeclarationRegex($methodName) { return sprintf('/public\s+(?:static\s+)?function\s+%s\s*\(.*\)\s*(?=\{)/i', $methodName); } /** * Gets the declaration code, as a string, for the passed method. * * @param array $namedParameters * * @return string */ private function getMethodDeclaration(Method $method, array $namedParameters) { $declaration = 'public'; $declaration .= $method->isStatic() ? ' static' : ''; $declaration .= ' function ' . $method->getName() . '('; foreach ($method->getParameters() as $index => $parameter) { $declaration .= $this->renderTypeHint($parameter); $name = $namedParameters[$index] ?? $parameter->getName(); $declaration .= '$' . $name; $declaration .= ','; } $declaration = rtrim($declaration, ','); $declaration .= ') '; $returnType = $method->getReturnType(); if ($returnType !== null) { $declaration .= sprintf(': %s', $returnType); } return $declaration; } /** * Returns the method original parameters, as they're * described in the $code string. * * @param int $code * * @return array */ private function getOriginalParameters($code, Method $method) { $matches = []; $parameterMatches = []; preg_match($this->getDeclarationRegex($method->getName()), $code, $matches); if ($matches !== []) { preg_match_all('/(?<=\$)(\w+)+/i', $matches[0], $parameterMatches); } $groupMatches = end($parameterMatches); return is_array($groupMatches) ? $groupMatches : [$groupMatches]; } /** * Checks if the method is declared within code. * * @param int $code * * @return bool */ private function isMethodWithinCode($code, Method $method) { return preg_match($this->getDeclarationRegex($method->getName()), $code) === 1; } } PKZw@f,Kmockery/library/Mockery/Generator/StringManipulation/Pass/ClassNamePass.phpnuW+AgetNamespaceName(); $namespace = ltrim($namespace, '\\'); $className = $config->getShortName(); $code = str_replace('namespace Mockery;', $namespace !== '' ? 'namespace ' . $namespace . ';' : '', $code); return str_replace('class Mock', 'class ' . $className, $code); } } PKZv??bmockery/library/Mockery/Generator/StringManipulation/Pass/RemoveBuiltinMethodsThatAreFinalPass.phpnuW+A '/public function __wakeup\(\)\s+\{.*?\}/sm', '__toString' => '/public function __toString\(\)\s+(:\s+string)?\s*\{.*?\}/sm', ]; /** * @param string $code * @return string */ public function apply($code, MockConfiguration $config) { $target = $config->getTargetClass(); if (! $target instanceof TargetClassInterface) { return $code; } foreach ($target->getMethods() as $method) { if (! $method->isFinal()) { continue; } if (! isset($this->methods[$method->getName()])) { continue; } $code = preg_replace($this->methods[$method->getName()], '', $code); } return $code; } } PKZ2ުKmockery/library/Mockery/Generator/StringManipulation/Pass/InterfacePass.phpnuW+AgetTargetInterfaces() as $i) { $name = ltrim($i->getName(), '\\'); if (! interface_exists($name)) { Mockery::declareInterface($name); } } $interfaces = array_reduce($config->getTargetInterfaces(), static function ($code, $i) { return $code . ', \\' . ltrim($i->getName(), '\\'); }, ''); return str_replace('implements MockInterface', 'implements MockInterface' . $interfaces, $code); } } PKZGmockery/library/Mockery/Generator/StringManipulation/Pass/TraitPass.phpnuW+AgetTargetTraits(); if ($traits === []) { return $code; } $useStatements = array_map(static function ($trait) { return 'use \\\\' . ltrim($trait->getName(), '\\') . ';'; }, $traits); return preg_replace('/^{$/m', "{\n " . implode("\n ", $useStatements) . "\n", $code); } } PKZsoRmockery/library/Mockery/Generator/StringManipulation/Pass/AvoidMethodClashPass.phpnuW+AgetName(); }, $config->getMethodsToMock()); foreach (['allows', 'expects'] as $method) { if (in_array($method, $names, true)) { $code = preg_replace(sprintf('#// start method %s.*// end method %s#ms', $method, $method), '', $code); $code = str_replace(' implements MockInterface', ' implements LegacyMockInterface', $code); } } return $code; } } PKZQ--Kmockery/library/Mockery/Generator/StringManipulation/Pass/ConstantsPass.phpnuW+AgetConstantsMap(); if ($cm === []) { return $code; } $name = $config->getName(); if (! array_key_exists($name, $cm)) { return $code; } $constantsCode = ''; foreach ($cm[$name] as $constant => $value) { $constantsCode .= sprintf("\n const %s = %s;\n", $constant, var_export($value, true)); } $offset = strrpos($code, '}'); if ($offset === false) { return $code; } return substr_replace($code, $constantsCode, $offset) . '}' . PHP_EOL; } } PKZ>vRmockery/library/Mockery/Generator/StringManipulation/Pass/MethodDefinitionPass.phpnuW+AgetMethodsToMock() as $method) { if ($method->isPublic()) { $methodDef = 'public'; } elseif ($method->isProtected()) { $methodDef = 'protected'; } else { $methodDef = 'private'; } if ($method->isStatic()) { $methodDef .= ' static'; } $methodDef .= ' function '; $methodDef .= $method->returnsReference() ? ' & ' : ''; $methodDef .= $method->getName(); $methodDef .= $this->renderParams($method, $config); $methodDef .= $this->renderReturnType($method); $methodDef .= $this->renderMethodBody($method, $config); $code = $this->appendToClass($code, $methodDef); } return $code; } protected function appendToClass($class, $code) { $lastBrace = strrpos($class, '}'); return substr($class, 0, $lastBrace) . $code . "\n }\n"; } protected function renderParams(Method $method, $config) { $class = $method->getDeclaringClass(); if ($class->isInternal()) { $overrides = $config->getParameterOverrides(); if (isset($overrides[strtolower($class->getName())][$method->getName()])) { return '(' . implode(',', $overrides[strtolower($class->getName())][$method->getName()]) . ')'; } } $methodParams = []; $params = $method->getParameters(); $isPhp81 = PHP_VERSION_ID >= 80100; foreach ($params as $param) { $paramDef = $this->renderTypeHint($param); $paramDef .= $param->isPassedByReference() ? '&' : ''; $paramDef .= $param->isVariadic() ? '...' : ''; $paramDef .= '$' . $param->getName(); if (! $param->isVariadic()) { if ($param->isDefaultValueAvailable() !== false) { $defaultValue = $param->getDefaultValue(); if (is_object($defaultValue)) { $prefix = get_class($defaultValue); if ($isPhp81) { if (enum_exists($prefix)) { $prefix = var_export($defaultValue, true); } elseif ( ! $param->isDefaultValueConstant() && // "Parameter #1 [ F\Q\CN $a = new \F\Q\CN(param1, param2: 2) ] preg_match( '#\s.*?\s=\snew\s(.*?)\s]$#', $param->__toString(), $matches ) === 1 ) { $prefix = 'new ' . $matches[1]; } } } else { $prefix = var_export($defaultValue, true); } $paramDef .= ' = ' . $prefix; } elseif ($param->isOptional()) { $paramDef .= ' = null'; } } $methodParams[] = $paramDef; } return '(' . implode(', ', $methodParams) . ')'; } protected function renderReturnType(Method $method) { $type = $method->getReturnType(); return $type ? sprintf(': %s', $type) : ''; } protected function renderTypeHint(Parameter $param) { $typeHint = $param->getTypeHint(); return $typeHint === null ? '' : sprintf('%s ', $typeHint); } private function renderMethodBody($method, $config) { $invoke = $method->isStatic() ? 'static::_mockery_handleStaticMethodCall' : '$this->_mockery_handleMethodCall'; $body = <<getDeclaringClass(); $class_name = strtolower($class->getName()); $overrides = $config->getParameterOverrides(); if (isset($overrides[$class_name][$method->getName()])) { $params = array_values($overrides[$class_name][$method->getName()]); $paramCount = count($params); for ($i = 0; $i < $paramCount; ++$i) { $param = $params[$i]; if (strpos($param, '&') !== false) { $body .= << {$i}) { \$argv[{$i}] = {$param}; } BODY; } } } else { $params = array_values($method->getParameters()); $paramCount = count($params); for ($i = 0; $i < $paramCount; ++$i) { $param = $params[$i]; if (! $param->isPassedByReference()) { continue; } $body .= << {$i}) { \$argv[{$i}] =& \${$param->getName()}; } BODY; } } $body .= "\$ret = {$invoke}(__FUNCTION__, \$argv);\n"; if (! in_array($method->getReturnType(), ['never', 'void'], true)) { $body .= "return \$ret;\n"; } return $body . "}\n"; } } PKZ^~Rmockery/library/Mockery/Generator/StringManipulation/Pass/RemoveDestructorPass.phpnuW+AgetTargetClass(); if (! $target) { return $code; } if (! $config->isMockOriginalDestructor()) { return preg_replace('/public function __destruct\(\)\s+\{.*?\}/sm', '', $code); } return $code; } } PKZ0aBmockery/library/Mockery/Generator/StringManipulation/Pass/Pass.phpnuW+A */ protected $cache = []; /** * @var Generator */ protected $generator; public function __construct(Generator $generator) { $this->generator = $generator; } /** * @return string */ public function generate(MockConfiguration $config) { $hash = $config->getHash(); if (array_key_exists($hash, $this->cache)) { return $this->cache[$hash]; } return $this->cache[$hash] = $this->generator->generate($config); } } PKZ—8mockery/library/Mockery/Generator/DefinedTargetClass.phpnuW+Arfc = $rfc; $this->name = $alias ?? $rfc->getName(); } /** * @return class-string */ public function __toString() { return $this->name; } /** * @param class-string $name * @param class-string|null $alias * @return self */ public static function factory($name, $alias = null) { return new self(new ReflectionClass($name), $alias); } /** * @return list */ public function getAttributes() { if (PHP_VERSION_ID < 80000) { return []; } return array_unique( array_merge( ['\AllowDynamicProperties'], array_map( static function (ReflectionAttribute $attribute): string { return '\\' . $attribute->getName(); }, $this->rfc->getAttributes() ) ) ); } /** * @return array */ public function getInterfaces() { return array_map( static function (ReflectionClass $interface): self { return new self($interface); }, $this->rfc->getInterfaces() ); } /** * @return list */ public function getMethods() { return array_map( static function (ReflectionMethod $method): Method { return new Method($method); }, $this->rfc->getMethods() ); } /** * @return class-string */ public function getName() { return $this->name; } /** * @return string */ public function getNamespaceName() { return $this->rfc->getNamespaceName(); } /** * @return string */ public function getShortName() { return $this->rfc->getShortName(); } /** * @return bool */ public function hasInternalAncestor() { if ($this->rfc->isInternal()) { return true; } $child = $this->rfc; while ($parent = $child->getParentClass()) { if ($parent->isInternal()) { return true; } $child = $parent; } return false; } /** * @param class-string $interface * @return bool */ public function implementsInterface($interface) { return $this->rfc->implementsInterface($interface); } /** * @return bool */ public function inNamespace() { return $this->rfc->inNamespace(); } /** * @return bool */ public function isAbstract() { return $this->rfc->isAbstract(); } /** * @return bool */ public function isFinal() { return $this->rfc->isFinal(); } } PKZ4/mockery/library/Mockery/Generator/Generator.phpnuW+Arfp = $rfp; } /** * Proxy all method calls to the reflection parameter. * * @template TMixed * @template TResult * * @param string $method * @param array $args * * @return TResult */ public function __call($method, array $args) { /** @var TResult */ return $this->rfp->{$method}(...$args); } /** * Get the reflection class for the parameter type, if it exists. * * This will be null if there was no type, or it was a scalar or a union. * * @return null|ReflectionClass * * @deprecated since 1.3.3 and will be removed in 2.0. */ public function getClass() { $typeHint = Reflector::getTypeHint($this->rfp, true); return class_exists($typeHint) ? DefinedTargetClass::factory($typeHint, false) : null; } /** * Get the name of the parameter. * * Some internal classes have funny looking definitions! * * @return string */ public function getName() { $name = $this->rfp->getName(); if (! $name || $name === '...') { return 'arg' . self::$parameterCounter++; } return $name; } /** * Get the string representation for the paramater type. * * @return null|string */ public function getTypeHint() { return Reflector::getTypeHint($this->rfp); } /** * Get the string representation for the paramater type. * * @return string * * @deprecated since 1.3.2 and will be removed in 2.0. Use getTypeHint() instead. */ public function getTypeHintAsString() { return (string) Reflector::getTypeHint($this->rfp, true); } /** * Determine if the parameter is an array. * * @return bool */ public function isArray() { return Reflector::isArray($this->rfp); } /** * Determine if the parameter is variadic. * * @return bool */ public function isVariadic() { return $this->rfp->isVariadic(); } } PKZ_GJJ,mockery/library/Mockery/Generator/Method.phpnuW+Amethod = $method; } /** * @template TArgs * @template TMixed * * @param string $method * @param array $args * * @return TMixed */ public function __call($method, $args) { /** @var TMixed */ return $this->method->{$method}(...$args); } /** * @return list */ public function getParameters() { return array_map(static function (ReflectionParameter $parameter) { return new Parameter($parameter); }, $this->method->getParameters()); } /** * @return null|string */ public function getReturnType() { return Reflector::getReturnType($this->method); } } PKZo>a5mockery/library/Mockery/Generator/MockNameBuilder.phpnuW+A */ protected $parts = []; /** * @param string $part */ public function addPart($part) { $this->parts[] = $part; return $this; } /** * @return string */ public function build() { $parts = ['Mockery', static::$mockCounter++]; foreach ($this->parts as $part) { $parts[] = str_replace('\\', '_', $part); } return implode('_', $parts); } } PKZ~uKuK7mockery/library/Mockery/Generator/MockConfiguration.phpnuW+A */ protected $allMethods = []; /** * Methods that should specifically not be mocked * * This is currently populated with stuff we don't know how to deal with, should really be somewhere else */ protected $blackListedMethods = []; protected $constantsMap = []; /** * An instance mock is where we override the original class before it's autoloaded * * @var bool */ protected $instanceMock = false; /** * If true, overrides original class destructor * * @var bool */ protected $mockOriginalDestructor = false; /** * The class name we'd like to use for a generated mock * * @var string|null */ protected $name; /** * Param overrides * * @var array */ protected $parameterOverrides = []; /** * A class that we'd like to mock * @var TargetClassInterface|null */ protected $targetClass; /** * @var class-string|null */ protected $targetClassName; /** * @var array */ protected $targetInterfaceNames = []; /** * A number of interfaces we'd like to mock, keyed by name to attempt to keep unique * * @var array */ protected $targetInterfaces = []; /** * An object we'd like our mock to proxy to * * @var object|null */ protected $targetObject; /** * @var array */ protected $targetTraitNames = []; /** * A number of traits we'd like to mock, keyed by name to attempt to keep unique * * @var array */ protected $targetTraits = []; /** * If not empty, only these methods will be mocked * * @var array */ protected $whiteListedMethods = []; /** * @param array $targets * @param array $blackListedMethods * @param array $whiteListedMethods * @param string|null $name * @param bool $instanceMock * @param array $parameterOverrides * @param bool $mockOriginalDestructor * @param array|scalar> $constantsMap */ public function __construct( array $targets = [], array $blackListedMethods = [], array $whiteListedMethods = [], $name = null, $instanceMock = false, array $parameterOverrides = [], $mockOriginalDestructor = false, array $constantsMap = [] ) { $this->addTargets($targets); $this->blackListedMethods = $blackListedMethods; $this->whiteListedMethods = $whiteListedMethods; $this->name = $name; $this->instanceMock = $instanceMock; $this->parameterOverrides = $parameterOverrides; $this->mockOriginalDestructor = $mockOriginalDestructor; $this->constantsMap = $constantsMap; } /** * Generate a suitable name based on the config * * @return string */ public function generateName() { $nameBuilder = new MockNameBuilder(); $targetObject = $this->getTargetObject(); if ($targetObject !== null) { $className = get_class($targetObject); $nameBuilder->addPart(strpos($className, '@') !== false ? md5($className) : $className); } $targetClass = $this->getTargetClass(); if ($targetClass instanceof TargetClassInterface) { $className = $targetClass->getName(); $nameBuilder->addPart(strpos($className, '@') !== false ? md5($className) : $className); } foreach ($this->getTargetInterfaces() as $targetInterface) { $nameBuilder->addPart($targetInterface->getName()); } return $nameBuilder->build(); } /** * @return array */ public function getBlackListedMethods() { return $this->blackListedMethods; } /** * @return array> */ public function getConstantsMap() { return $this->constantsMap; } /** * Attempt to create a hash of the configuration, in order to allow caching * * @TODO workout if this will work * * @return string */ public function getHash() { $vars = [ 'targetClassName' => $this->targetClassName, 'targetInterfaceNames' => $this->targetInterfaceNames, 'targetTraitNames' => $this->targetTraitNames, 'name' => $this->name, 'blackListedMethods' => $this->blackListedMethods, 'whiteListedMethod' => $this->whiteListedMethods, 'instanceMock' => $this->instanceMock, 'parameterOverrides' => $this->parameterOverrides, 'mockOriginalDestructor' => $this->mockOriginalDestructor, ]; return md5(serialize($vars)); } /** * Gets a list of methods from the classes, interfaces and objects and filters them appropriately. * Lot's of filtering going on, perhaps we could have filter classes to iterate through * * @return list */ public function getMethodsToMock() { $methods = $this->getAllMethods(); foreach ($methods as $key => $method) { if ($method->isFinal()) { unset($methods[$key]); } } /** * Whitelist trumps everything else */ $whiteListedMethods = $this->getWhiteListedMethods(); if ($whiteListedMethods !== []) { $whitelist = array_map('strtolower', $whiteListedMethods); return array_filter($methods, static function ($method) use ($whitelist) { if ($method->isAbstract()) { return true; } return in_array(strtolower($method->getName()), $whitelist, true); }); } /** * Remove blacklisted methods */ $blackListedMethods = $this->getBlackListedMethods(); if ($blackListedMethods !== []) { $blacklist = array_map('strtolower', $blackListedMethods); $methods = array_filter($methods, static function ($method) use ($blacklist) { return ! in_array(strtolower($method->getName()), $blacklist, true); }); } /** * Internal objects can not be instantiated with newInstanceArgs and if * they implement Serializable, unserialize will have to be called. As * such, we can't mock it and will need a pass to add a dummy * implementation */ $targetClass = $this->getTargetClass(); if ( $targetClass !== null && $targetClass->implementsInterface(Serializable::class) && $targetClass->hasInternalAncestor() ) { $methods = array_filter($methods, static function ($method) { return $method->getName() !== 'unserialize'; }); } return array_values($methods); } /** * @return string|null */ public function getName() { return $this->name; } /** * @return string */ public function getNamespaceName() { $parts = explode('\\', $this->getName()); array_pop($parts); if ($parts !== []) { return implode('\\', $parts); } return ''; } /** * @return array */ public function getParameterOverrides() { return $this->parameterOverrides; } /** * @return string */ public function getShortName() { $parts = explode('\\', $this->getName()); return array_pop($parts); } /** * @return null|TargetClassInterface */ public function getTargetClass() { if ($this->targetClass) { return $this->targetClass; } if (! $this->targetClassName) { return null; } if (class_exists($this->targetClassName)) { $alias = null; if (strpos($this->targetClassName, '@') !== false) { $alias = (new MockNameBuilder()) ->addPart('anonymous_class') ->addPart(md5($this->targetClassName)) ->build(); class_alias($this->targetClassName, $alias); } $dtc = DefinedTargetClass::factory($this->targetClassName, $alias); if ($this->getTargetObject() === null && $dtc->isFinal()) { throw new Exception( 'The class ' . $this->targetClassName . ' is marked final and its methods' . ' cannot be replaced. Classes marked final can be passed in' . ' to \Mockery::mock() as instantiated objects to create a' . ' partial mock, but only if the mock is not subject to type' . ' hinting checks.' ); } $this->targetClass = $dtc; } else { $this->targetClass = UndefinedTargetClass::factory($this->targetClassName); } return $this->targetClass; } /** * @return class-string|null */ public function getTargetClassName() { return $this->targetClassName; } /** * @return list */ public function getTargetInterfaces() { if ($this->targetInterfaces !== []) { return $this->targetInterfaces; } foreach ($this->targetInterfaceNames as $targetInterface) { if (! interface_exists($targetInterface)) { $this->targetInterfaces[] = UndefinedTargetClass::factory($targetInterface); continue; } $dtc = DefinedTargetClass::factory($targetInterface); $extendedInterfaces = array_keys($dtc->getInterfaces()); $extendedInterfaces[] = $targetInterface; $traversableFound = false; $iteratorShiftedToFront = false; foreach ($extendedInterfaces as $interface) { if (! $traversableFound && preg_match('/^\\?Iterator(|Aggregate)$/i', $interface)) { break; } if (preg_match('/^\\\\?IteratorAggregate$/i', $interface)) { $this->targetInterfaces[] = DefinedTargetClass::factory('\\IteratorAggregate'); $iteratorShiftedToFront = true; continue; } if (preg_match('/^\\\\?Iterator$/i', $interface)) { $this->targetInterfaces[] = DefinedTargetClass::factory('\\Iterator'); $iteratorShiftedToFront = true; continue; } if (preg_match('/^\\\\?Traversable$/i', $interface)) { $traversableFound = true; } } if ($traversableFound && ! $iteratorShiftedToFront) { $this->targetInterfaces[] = DefinedTargetClass::factory('\\IteratorAggregate'); } /** * We never straight up implement Traversable */ $isTraversable = preg_match('/^\\\\?Traversable$/i', $targetInterface); if ($isTraversable === 0 || $isTraversable === false) { $this->targetInterfaces[] = $dtc; } } return $this->targetInterfaces = array_unique($this->targetInterfaces); } /** * @return object|null */ public function getTargetObject() { return $this->targetObject; } /** * @return list */ public function getTargetTraits() { if ($this->targetTraits !== []) { return $this->targetTraits; } foreach ($this->targetTraitNames as $targetTrait) { $this->targetTraits[] = DefinedTargetClass::factory($targetTrait); } $this->targetTraits = array_unique($this->targetTraits); // just in case return $this->targetTraits; } /** * @return array */ public function getWhiteListedMethods() { return $this->whiteListedMethods; } /** * @return bool */ public function isInstanceMock() { return $this->instanceMock; } /** * @return bool */ public function isMockOriginalDestructor() { return $this->mockOriginalDestructor; } /** * @param class-string $className * @return self */ public function rename($className) { $targets = []; if ($this->targetClassName) { $targets[] = $this->targetClassName; } if ($this->targetInterfaceNames) { $targets = array_merge($targets, $this->targetInterfaceNames); } if ($this->targetTraitNames) { $targets = array_merge($targets, $this->targetTraitNames); } if ($this->targetObject) { $targets[] = $this->targetObject; } return new self( $targets, $this->blackListedMethods, $this->whiteListedMethods, $className, $this->instanceMock, $this->parameterOverrides, $this->mockOriginalDestructor, $this->constantsMap ); } /** * We declare the __callStatic method to handle undefined stuff, if the class * we're mocking has also defined it, we need to comply with their interface * * @return bool */ public function requiresCallStaticTypeHintRemoval() { foreach ($this->getAllMethods() as $method) { if ($method->getName() === '__callStatic') { $params = $method->getParameters(); if (! array_key_exists(1, $params)) { return false; } return ! $params[1]->isArray(); } } return false; } /** * We declare the __call method to handle undefined stuff, if the class * we're mocking has also defined it, we need to comply with their interface * * @return bool */ public function requiresCallTypeHintRemoval() { foreach ($this->getAllMethods() as $method) { if ($method->getName() === '__call') { $params = $method->getParameters(); return ! $params[1]->isArray(); } } return false; } /** * @param class-string|object $target */ protected function addTarget($target) { if (is_object($target)) { $this->setTargetObject($target); $this->setTargetClassName(get_class($target)); return; } if ($target[0] !== '\\') { $target = '\\' . $target; } if (class_exists($target)) { $this->setTargetClassName($target); return; } if (interface_exists($target)) { $this->addTargetInterfaceName($target); return; } if (trait_exists($target)) { $this->addTargetTraitName($target); return; } /** * Default is to set as class, or interface if class already set * * Don't like this condition, can't remember what the default * targetClass is for */ if ($this->getTargetClassName()) { $this->addTargetInterfaceName($target); return; } $this->setTargetClassName($target); } /** * If we attempt to implement Traversable, * we must ensure we are also implementing either Iterator or IteratorAggregate, * and that whichever one it is comes before Traversable in the list of implements. * * @param class-string $targetInterface */ protected function addTargetInterfaceName($targetInterface) { $this->targetInterfaceNames[] = $targetInterface; } /** * @param array $interfaces */ protected function addTargets($interfaces) { foreach ($interfaces as $interface) { $this->addTarget($interface); } } /** * @param class-string $targetTraitName */ protected function addTargetTraitName($targetTraitName) { $this->targetTraitNames[] = $targetTraitName; } /** * @return list */ protected function getAllMethods() { if ($this->allMethods) { return $this->allMethods; } $classes = $this->getTargetInterfaces(); if ($this->getTargetClass()) { $classes[] = $this->getTargetClass(); } $methods = []; foreach ($classes as $class) { $methods = array_merge($methods, $class->getMethods()); } foreach ($this->getTargetTraits() as $trait) { foreach ($trait->getMethods() as $method) { if ($method->isAbstract()) { $methods[] = $method; } } } $names = []; $methods = array_filter($methods, static function ($method) use (&$names) { if (in_array($method->getName(), $names, true)) { return false; } $names[] = $method->getName(); return true; }); return $this->allMethods = $methods; } /** * @param class-string $targetClassName */ protected function setTargetClassName($targetClassName) { $this->targetClassName = $targetClassName; } /** * @param object $object */ protected function setTargetObject($object) { $this->targetObject = $object; } } PKZ *4mockery/library/Mockery/Generator/MockDefinition.phpnuW+AgetName()) { throw new InvalidArgumentException('MockConfiguration must contain a name'); } $this->config = $config; $this->code = $code; } /** * @return string */ public function getClassName() { return $this->config->getName(); } /** * @return string */ public function getCode() { return $this->code; } /** * @return MockConfiguration */ public function getConfig() { return $this->config; } } PKZ cZ Z :mockery/library/Mockery/Generator/UndefinedTargetClass.phpnuW+Aname = $name; } /** * @return class-string */ public function __toString() { return $this->name; } /** * @param class-string $name * @return self */ public static function factory($name) { return new self($name); } /** * @return list */ public function getAttributes() { return []; } /** * @return list */ public function getInterfaces() { return []; } /** * @return list */ public function getMethods() { return []; } /** * @return class-string */ public function getName() { return $this->name; } /** * @return string */ public function getNamespaceName() { $parts = explode('\\', ltrim($this->getName(), '\\')); array_pop($parts); return implode('\\', $parts); } /** * @return string */ public function getShortName() { $parts = explode('\\', $this->getName()); return array_pop($parts); } /** * @return bool */ public function hasInternalAncestor() { return false; } /** * @param class-string $interface * @return bool */ public function implementsInterface($interface) { return false; } /** * @return bool */ public function inNamespace() { return $this->getNamespaceName() !== ''; } /** * @return bool */ public function isAbstract() { return false; } /** * @return bool */ public function isFinal() { return false; } } PKZw]0mockery/library/Mockery/ExpectationInterface.phpnuW+A */ public const BUILTIN_TYPES = ['array', 'bool', 'int', 'float', 'null', 'object', 'string']; /** * List of reserved words. * * @var list */ public const RESERVED_WORDS = ['bool', 'true', 'false', 'float', 'int', 'iterable', 'mixed', 'never', 'null', 'object', 'string', 'void']; /** * Iterable. * * @var list */ private const ITERABLE = ['iterable']; /** * Traversable array. * * @var list */ private const TRAVERSABLE_ARRAY = ['\Traversable', 'array']; /** * Compute the string representation for the return type. * * @param bool $withoutNullable * * @return null|string */ public static function getReturnType(ReflectionMethod $method, $withoutNullable = false) { $type = $method->getReturnType(); if (! $type instanceof ReflectionType && method_exists($method, 'getTentativeReturnType')) { $type = $method->getTentativeReturnType(); } if (! $type instanceof ReflectionType) { return null; } $typeHint = self::getTypeFromReflectionType($type, $method->getDeclaringClass()); return (! $withoutNullable && $type->allowsNull()) ? self::formatNullableType($typeHint) : $typeHint; } /** * Compute the string representation for the simplest return type. * * @return null|string */ public static function getSimplestReturnType(ReflectionMethod $method) { $type = $method->getReturnType(); if (! $type instanceof ReflectionType && method_exists($method, 'getTentativeReturnType')) { $type = $method->getTentativeReturnType(); } if (! $type instanceof ReflectionType || $type->allowsNull()) { return null; } $typeInformation = self::getTypeInformation($type, $method->getDeclaringClass()); // return the first primitive type hint foreach ($typeInformation as $info) { if ($info['isPrimitive']) { return $info['typeHint']; } } // if no primitive type, return the first type foreach ($typeInformation as $info) { return $info['typeHint']; } return null; } /** * Compute the string representation for the paramater type. * * @param bool $withoutNullable * * @return null|string */ public static function getTypeHint(ReflectionParameter $param, $withoutNullable = false) { if (! $param->hasType()) { return null; } $type = $param->getType(); $declaringClass = $param->getDeclaringClass(); $typeHint = self::getTypeFromReflectionType($type, $declaringClass); return (! $withoutNullable && $type->allowsNull()) ? self::formatNullableType($typeHint) : $typeHint; } /** * Determine if the parameter is typed as an array. * * @return bool */ public static function isArray(ReflectionParameter $param) { $type = $param->getType(); return $type instanceof ReflectionNamedType && $type->getName(); } /** * Determine if the given type is a reserved word. */ public static function isReservedWord(string $type): bool { return in_array(strtolower($type), self::RESERVED_WORDS, true); } /** * Format the given type as a nullable type. */ private static function formatNullableType(string $typeHint): string { if ($typeHint === 'mixed') { return $typeHint; } if (strpos($typeHint, 'null') !== false) { return $typeHint; } if (PHP_VERSION_ID < 80000) { return sprintf('?%s', $typeHint); } return sprintf('%s|null', $typeHint); } private static function getTypeFromReflectionType(ReflectionType $type, ReflectionClass $declaringClass): string { if ($type instanceof ReflectionNamedType) { $typeHint = $type->getName(); if ($type->isBuiltin()) { return $typeHint; } if ($typeHint === 'static') { return $typeHint; } // 'self' needs to be resolved to the name of the declaring class if ($typeHint === 'self') { $typeHint = $declaringClass->getName(); } // 'parent' needs to be resolved to the name of the parent class if ($typeHint === 'parent') { $typeHint = $declaringClass->getParentClass()->getName(); } // class names need prefixing with a slash return sprintf('\\%s', $typeHint); } if ($type instanceof ReflectionIntersectionType) { $types = array_map( static function (ReflectionType $type) use ($declaringClass): string { return self::getTypeFromReflectionType($type, $declaringClass); }, $type->getTypes() ); return implode('&', $types); } if ($type instanceof ReflectionUnionType) { $types = array_map( static function (ReflectionType $type) use ($declaringClass): string { return self::getTypeFromReflectionType($type, $declaringClass); }, $type->getTypes() ); $intersect = array_intersect(self::TRAVERSABLE_ARRAY, $types); if ($intersect === self::TRAVERSABLE_ARRAY) { $types = array_merge(self::ITERABLE, array_diff($types, self::TRAVERSABLE_ARRAY)); } return implode( '|', array_map( static function (string $type): string { return strpos($type, '&') === false ? $type : sprintf('(%s)', $type); }, $types ) ); } throw new InvalidArgumentException('Unknown ReflectionType: ' . get_debug_type($type)); } /** * Get the string representation of the given type. * * @return list */ private static function getTypeInformation(ReflectionType $type, ReflectionClass $declaringClass): array { // PHP 8 union types and PHP 8.1 intersection types can be recursively processed if ($type instanceof ReflectionUnionType || $type instanceof ReflectionIntersectionType) { $types = []; foreach ($type->getTypes() as $innterType) { foreach (self::getTypeInformation($innterType, $declaringClass) as $info) { if ($info['typeHint'] === 'null' && $info['isPrimitive']) { continue; } $types[] = $info; } } return $types; } // $type must be an instance of \ReflectionNamedType $typeHint = $type->getName(); // builtins can be returned as is if ($type->isBuiltin()) { return [ [ 'typeHint' => $typeHint, 'isPrimitive' => in_array($typeHint, self::BUILTIN_TYPES, true), ], ]; } // 'static' can be returned as is if ($typeHint === 'static') { return [ [ 'typeHint' => $typeHint, 'isPrimitive' => false, ], ]; } // 'self' needs to be resolved to the name of the declaring class if ($typeHint === 'self') { $typeHint = $declaringClass->getName(); } // 'parent' needs to be resolved to the name of the parent class if ($typeHint === 'parent') { $typeHint = $declaringClass->getParentClass()->getName(); } // class names need prefixing with a slash return [ [ 'typeHint' => sprintf('\\%s', $typeHint), 'isPrimitive' => false, ], ]; } } PKZeA800%mockery/library/Mockery/Undefined.phpnuW+Amethod = $method; $this->args = $args; } /** * @return array */ public function getArgs() { return $this->args; } /** * @return string */ public function getMethod() { return $this->method; } } PKZ9mockery/library/Mockery/QuickDefinitionsConfiguration.phpnuW+A_quickDefinitionsApplicationMode = $newValue ? self::QUICK_DEFINITIONS_MODE_MOCK_AT_LEAST_ONCE : self::QUICK_DEFINITIONS_MODE_DEFAULT_EXPECTATION; } return $this->_quickDefinitionsApplicationMode === self::QUICK_DEFINITIONS_MODE_MOCK_AT_LEAST_ONCE; } } PKZyA =mockery/library/Mockery/Adapter/Phpunit/TestListenerTrait.phpnuW+AgetStatus() !== BaseTestRunner::STATUS_PASSED) { // If the test didn't pass there is no guarantee that // verifyMockObjects and assertPostConditions have been called. // And even if it did, the point here is to prevent false // negatives, not to make failing tests fail for more reasons. return; } try { // The self() call is used as a sentinel. Anything that throws if // the container is closed already will do. Mockery::self(); } catch (LogicException $logicException) { return; } $e = new ExpectationFailedException( sprintf( "Mockery's expectations have not been verified. Make sure that \Mockery::close() is called at the end of the test. Consider using %s\MockeryPHPUnitIntegration or extending %s\MockeryTestCase.", __NAMESPACE__, __NAMESPACE__ ) ); /** @var \PHPUnit\Framework\TestResult $result */ $result = $test->getTestResultObject(); if ($result !== null) { $result->addFailure($test, $e, $time); } } public function startTestSuite() { if (method_exists(Blacklist::class, 'addDirectory')) { (new Blacklist())->getBlacklistedDirectories(); Blacklist::addDirectory(dirname((new ReflectionClass(Mockery::class))->getFileName())); } else { Blacklist::$blacklistedClassNames[Mockery::class] = 1; } } } PKZR??Emockery/library/Mockery/Adapter/Phpunit/MockeryPHPUnitIntegration.phpnuW+AaddToAssertionCount(Mockery::getContainer()->mockery_getExpectationCount()); } protected function checkMockeryExceptions() { if (! method_exists($this, 'markAsRisky')) { return; } foreach (Mockery::getContainer()->mockery_thrownExceptions() as $e) { if (! $e->dismissed()) { $this->markAsRisky(); } } } protected function closeMockery() { Mockery::close(); $this->mockeryOpen = false; } /** * Performs assertions shared by all tests of a test case. This method is * called before execution of a test ends and before the tearDown method. */ protected function mockeryAssertPostConditions() { $this->addMockeryExpectationsToAssertionCount(); $this->checkMockeryExceptions(); $this->closeMockery(); parent::assertPostConditions(); } /** * @after */ #[After] protected function purgeMockeryContainer() { if ($this->mockeryOpen) { // post conditions wasn't called, so test probably failed Mockery::close(); } } /** * @before */ #[Before] protected function startMockery() { $this->mockeryOpen = true; } } PKZRR;mockery/library/Mockery/Adapter/Phpunit/MockeryTestCase.phpnuW+Atrait = new TestListenerTrait(); } public function endTest(Test $test, float $time): void { $this->trait->endTest($test, $time); } public function startTestSuite(TestSuite $suite): void { $this->trait->startTestSuite(); } } PKZ^ hh@mockery/library/Mockery/Adapter/Phpunit/MockeryTestCaseSetUp.phpnuW+AmockeryTestSetUp(); } protected function tearDown(): void { $this->mockeryTestTearDown(); parent::tearDown(); } } PKZN*  Ymockery/library/Mockery/Adapter/Phpunit/MockeryPHPUnitIntegrationAssertPostConditions.phpnuW+AmockeryAssertPostConditions(); } } PKZӺ^(mockery/library/Mockery/Instantiator.phpnuW+A $className * * @throws InvalidArgumentException * @throws UnexpectedValueException * * @return TClass */ public function instantiate($className): object { return $this->buildFactory($className)(); } /** * @throws UnexpectedValueException */ private function attemptInstantiationViaUnSerialization( ReflectionClass $reflectionClass, string $serializedString ): void { set_error_handler(static function ($code, $message, $file, $line) use ($reflectionClass, &$error): void { $msg = sprintf( 'Could not produce an instance of "%s" via un-serialization, since an error was triggered in file "%s" at line "%d"', $reflectionClass->getName(), $file, $line ); $error = new UnexpectedValueException($msg, 0, new Exception($message, $code)); }); try { unserialize($serializedString); } catch (Exception $exception) { restore_error_handler(); throw new UnexpectedValueException( sprintf( 'An exception was raised while trying to instantiate an instance of "%s" via un-serialization', $reflectionClass->getName() ), 0, $exception ); } restore_error_handler(); if ($error instanceof UnexpectedValueException) { throw $error; } } /** * Builds a {@see Closure} capable of instantiating the given $className without invoking its constructor. */ private function buildFactory(string $className): Closure { $reflectionClass = $this->getReflectionClass($className); if ($this->isInstantiableViaReflection($reflectionClass)) { return static function () use ($reflectionClass) { return $reflectionClass->newInstanceWithoutConstructor(); }; } $serializedString = sprintf('O:%d:"%s":0:{}', strlen($className), $className); $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); return static function () use ($serializedString) { return unserialize($serializedString); }; } /** * @throws InvalidArgumentException */ private function getReflectionClass(string $className): ReflectionClass { if (! class_exists($className)) { throw new InvalidArgumentException(sprintf('Class:%s does not exist', $className)); } $reflection = new ReflectionClass($className); if ($reflection->isAbstract()) { throw new InvalidArgumentException(sprintf('Class:%s is an abstract class', $className)); } return $reflection; } /** * Verifies whether the given class is to be considered internal */ private function hasInternalAncestors(ReflectionClass $reflectionClass): bool { do { if ($reflectionClass->isInternal()) { return true; } } while ($reflectionClass = $reflectionClass->getParentClass()); return false; } /** * Verifies if the class is instantiable via reflection */ private function isInstantiableViaReflection(ReflectionClass $reflectionClass): bool { return ! ($reflectionClass->isInternal() && $reflectionClass->isFinal()); } } PKZ| 1f1fmockery/library/Mockery.phpnuW+A */ private static $_filesToCleanUp = []; /** * Return instance of AndAnyOtherArgs matcher. * * @return AndAnyOtherArgs */ public static function andAnyOtherArgs() { return new AndAnyOtherArgs(); } /** * Return instance of AndAnyOtherArgs matcher. * * An alternative name to `andAnyOtherArgs` so * the API stays closer to `any` as well. * * @return AndAnyOtherArgs */ public static function andAnyOthers() { return new AndAnyOtherArgs(); } /** * Return instance of ANY matcher. * * @return Any */ public static function any() { return new Any(); } /** * Return instance of ANYOF matcher. * * @template TAnyOf * * @param TAnyOf ...$args * * @return AnyOf */ public static function anyOf(...$args) { return new AnyOf($args); } /** * @return array * * @deprecated since 1.3.2 and will be removed in 2.0. */ public static function builtInTypes() { return ['array', 'bool', 'callable', 'float', 'int', 'iterable', 'object', 'self', 'string', 'void']; } /** * Return instance of CLOSURE matcher. * * @template TReference * * @param TReference $reference * * @return ClosureMatcher */ public static function capture(&$reference) { $closure = static function ($argument) use (&$reference) { $reference = $argument; return true; }; return new ClosureMatcher($closure); } /** * Static shortcut to closing up and verifying all mocks in the global * container, and resetting the container static variable to null. * * @return void */ public static function close() { foreach (self::$_filesToCleanUp as $fileName) { @\unlink($fileName); } self::$_filesToCleanUp = []; if (self::$_container === null) { return; } $container = self::$_container; self::$_container = null; $container->mockery_teardown(); $container->mockery_close(); } /** * Return instance of CONTAINS matcher. * * @template TContains * * @param TContains $args * * @return Contains */ public static function contains(...$args) { return new Contains($args); } /** * @param class-string $fqn * * @return void */ public static function declareClass($fqn) { static::declareType($fqn, 'class'); } /** * @param class-string $fqn * * @return void */ public static function declareInterface($fqn) { static::declareType($fqn, 'interface'); } /** * Return instance of DUCKTYPE matcher. * * @template TDucktype * * @param TDucktype ...$args * * @return Ducktype */ public static function ducktype(...$args) { return new Ducktype($args); } /** * Static fetching of a mock associated with a name or explicit class poser. * * @template TFetchMock of object * * @param class-string $name * * @return null|(LegacyMockInterface&MockInterface&TFetchMock) */ public static function fetchMock($name) { return self::getContainer()->fetchMock($name); } /** * Utility method to format method name and arguments into a string. * * @param string $method * * @return string */ public static function formatArgs($method, ?array $arguments = null) { if ($arguments === null) { return $method . '()'; } $formattedArguments = []; foreach ($arguments as $argument) { $formattedArguments[] = self::formatArgument($argument); } return $method . '(' . \implode(', ', $formattedArguments) . ')'; } /** * Utility function to format objects to printable arrays. * * @return string */ public static function formatObjects(?array $objects = null) { static $formatting; if ($formatting) { return '[Recursion]'; } if ($objects === null) { return ''; } $objects = \array_filter($objects, 'is_object'); if ($objects === []) { return ''; } $formatting = true; $parts = []; foreach ($objects as $object) { $parts[\get_class($object)] = self::objectToArray($object); } $formatting = false; return 'Objects: ( ' . \var_export($parts, true) . ')'; } /** * Lazy loader and Getter for the global * configuration container. * * @return Configuration */ public static function getConfiguration() { if (self::$_config === null) { self::$_config = new Configuration(); } return self::$_config; } /** * Lazy loader and getter for the container property. * * @return Container */ public static function getContainer() { if (self::$_container === null) { self::$_container = new Container(self::getGenerator(), self::getLoader()); } return self::$_container; } /** * Creates and returns a default generator * used inside this class. * * @return CachingGenerator */ public static function getDefaultGenerator() { return new CachingGenerator(StringManipulationGenerator::withDefaultPasses()); } /** * Gets an EvalLoader to be used as default. * * @return EvalLoader */ public static function getDefaultLoader() { return new EvalLoader(); } /** * Lazy loader method and getter for * the generator property. * * @return Generator */ public static function getGenerator() { if (self::$_generator === null) { self::$_generator = self::getDefaultGenerator(); } return self::$_generator; } /** * Lazy loader method and getter for * the $_loader property. * * @return Loader */ public static function getLoader() { if (self::$_loader === null) { self::$_loader = self::getDefaultLoader(); } return self::$_loader; } /** * Defines the global helper functions * * @return void */ public static function globalHelpers() { require_once __DIR__ . '/helpers.php'; } /** * Return instance of HASKEY matcher. * * @template THasKey * * @param THasKey $key * * @return HasKey */ public static function hasKey($key) { return new HasKey($key); } /** * Return instance of HASVALUE matcher. * * @template THasValue * * @param THasValue $val * * @return HasValue */ public static function hasValue($val) { return new HasValue($val); } /** * Static and Semantic shortcut to Container::mock(). * * @template TInstanceMock * * @param array|TInstanceMock|array> $args * * @return LegacyMockInterface&MockInterface&TInstanceMock */ public static function instanceMock(...$args) { return self::getContainer()->mock(...$args); } /** * @param string $type * * @return bool * * @deprecated since 1.3.2 and will be removed in 2.0. */ public static function isBuiltInType($type) { return \in_array($type, self::builtInTypes(), true); } /** * Return instance of IsEqual matcher. * * @template TExpected * * @param TExpected $expected */ public static function isEqual($expected): IsEqual { return new IsEqual($expected); } /** * Return instance of IsSame matcher. * * @template TExpected * * @param TExpected $expected */ public static function isSame($expected): IsSame { return new IsSame($expected); } /** * Static shortcut to Container::mock(). * * @template TMock of object * * @param array|TMock|Closure(LegacyMockInterface&MockInterface&TMock):LegacyMockInterface&MockInterface&TMock|array> $args * * @return LegacyMockInterface&MockInterface&TMock */ public static function mock(...$args) { return self::getContainer()->mock(...$args); } /** * Return instance of MUSTBE matcher. * * @template TExpected * * @param TExpected $expected * * @return MustBe */ public static function mustBe($expected) { return new MustBe($expected); } /** * Static shortcut to Container::mock(), first argument names the mock. * * @template TNamedMock * * @param array|TNamedMock|array> $args * * @return LegacyMockInterface&MockInterface&TNamedMock */ public static function namedMock(...$args) { $name = \array_shift($args); $builder = new MockConfigurationBuilder(); $builder->setName($name); \array_unshift($args, $builder); return self::getContainer()->mock(...$args); } /** * Return instance of NOT matcher. * * @template TNotExpected * * @param TNotExpected $expected * * @return Not */ public static function not($expected) { return new Not($expected); } /** * Return instance of NOTANYOF matcher. * * @template TNotAnyOf * * @param TNotAnyOf ...$args * * @return NotAnyOf */ public static function notAnyOf(...$args) { return new NotAnyOf($args); } /** * Return instance of CLOSURE matcher. * * @template TClosure of Closure * * @param TClosure $closure * * @return ClosureMatcher */ public static function on($closure) { return new ClosureMatcher($closure); } /** * Utility function to parse shouldReceive() arguments and generate * expectations from such as needed. * * @template TReturnArgs * * @param TReturnArgs ...$args * @param Closure $add * * @return CompositeExpectation */ public static function parseShouldReturnArgs(LegacyMockInterface $mock, $args, $add) { $composite = new CompositeExpectation(); foreach ($args as $arg) { if (\is_string($arg)) { $composite->add(self::buildDemeterChain($mock, $arg, $add)); continue; } if (\is_array($arg)) { foreach ($arg as $k => $v) { $composite->add(self::buildDemeterChain($mock, $k, $add)->andReturn($v)); } } } return $composite; } /** * Return instance of PATTERN matcher. * * @template TPatter * * @param TPatter $expected * * @return Pattern */ public static function pattern($expected) { return new Pattern($expected); } /** * Register a file to be deleted on tearDown. * * @param string $fileName */ public static function registerFileForCleanUp($fileName) { self::$_filesToCleanUp[] = $fileName; } /** * Reset the container to null. * * @return void */ public static function resetContainer() { self::$_container = null; } /** * Static shortcut to Container::self(). * * @throws LogicException * * @return LegacyMockInterface|MockInterface */ public static function self() { if (self::$_container === null) { throw new LogicException('You have not declared any mocks yet'); } return self::$_container->self(); } /** * Set the container. * * @return Container */ public static function setContainer(Container $container) { return self::$_container = $container; } /** * Setter for the $_generator static property. */ public static function setGenerator(Generator $generator) { self::$_generator = $generator; } /** * Setter for the $_loader static property. */ public static function setLoader(Loader $loader) { self::$_loader = $loader; } /** * Static and semantic shortcut for getting a mock from the container * and applying the spy's expected behavior into it. * * @template TSpy * * @param array|TSpy|Closure(LegacyMockInterface&MockInterface&TSpy):LegacyMockInterface&MockInterface&TSpy|array> $args * * @return LegacyMockInterface&MockInterface&TSpy */ public static function spy(...$args) { if ($args !== [] && $args[0] instanceof Closure) { $args[0] = new ClosureWrapper($args[0]); } return self::getContainer()->mock(...$args)->shouldIgnoreMissing(); } /** * Return instance of SUBSET matcher. * * @param bool $strict - (Optional) True for strict comparison, false for loose * * @return Subset */ public static function subset(array $part, $strict = true) { return new Subset($part, $strict); } /** * Return instance of TYPE matcher. * * @template TExpectedType * * @param TExpectedType $expected * * @return Type */ public static function type($expected) { return new Type($expected); } /** * Sets up expectations on the members of the CompositeExpectation and * builds up any demeter chain that was passed to shouldReceive. * * @param string $arg * @param Closure $add * * @throws MockeryException * * @return ExpectationInterface */ protected static function buildDemeterChain(LegacyMockInterface $mock, $arg, $add) { $container = $mock->mockery_getContainer(); $methodNames = \explode('->', $arg); \reset($methodNames); if ( ! $mock->mockery_isAnonymous() && ! self::getConfiguration()->mockingNonExistentMethodsAllowed() && ! \in_array(\current($methodNames), $mock->mockery_getMockableMethods(), true) ) { throw new MockeryException( "Mockery's configuration currently forbids mocking the method " . \current($methodNames) . ' as it does not exist on the class or object ' . 'being mocked' ); } /** @var Closure $nextExp */ $nextExp = static function ($method) use ($add) { return $add($method); }; $parent = \get_class($mock); /** @var null|ExpectationInterface $expectations */ $expectations = null; while (true) { $method = \array_shift($methodNames); $expectations = $mock->mockery_getExpectationsFor($method); if ($expectations === null || self::noMoreElementsInChain($methodNames)) { $expectations = $nextExp($method); if (self::noMoreElementsInChain($methodNames)) { break; } $mock = self::getNewDemeterMock($container, $parent, $method, $expectations); } else { $demeterMockKey = $container->getKeyOfDemeterMockFor($method, $parent); if ($demeterMockKey !== null) { $mock = self::getExistingDemeterMock($container, $demeterMockKey); } } $parent .= '->' . $method; $nextExp = static function ($n) use ($mock) { return $mock->allows($n); }; } return $expectations; } /** * Utility method for recursively generating a representation of the given array. * * @template TArray or array * * @param TArray $argument * @param int $nesting * * @return TArray */ private static function cleanupArray($argument, $nesting = 3) { if ($nesting === 0) { return '...'; } foreach ($argument as $key => $value) { if (\is_array($value)) { $argument[$key] = self::cleanupArray($value, $nesting - 1); continue; } if (\is_object($value)) { $argument[$key] = self::objectToArray($value, $nesting - 1); } } return $argument; } /** * Utility method used for recursively generating * an object or array representation. * * @template TArgument * * @param TArgument $argument * @param int $nesting * * @return mixed */ private static function cleanupNesting($argument, $nesting) { if (\is_object($argument)) { $object = self::objectToArray($argument, $nesting - 1); $object['class'] = \get_class($argument); return $object; } if (\is_array($argument)) { return self::cleanupArray($argument, $nesting - 1); } return $argument; } /** * @param string $fqn * @param string $type */ private static function declareType($fqn, $type): void { $targetCode = ' */ private static function extractInstancePublicProperties($object, $nesting) { $reflection = new ReflectionClass($object); $properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC); $cleanedProperties = []; foreach ($properties as $publicProperty) { if (! $publicProperty->isStatic()) { $name = $publicProperty->getName(); try { $cleanedProperties[$name] = self::cleanupNesting($object->{$name}, $nesting); } catch (Exception $exception) { $cleanedProperties[$name] = $exception->getMessage(); } } } return $cleanedProperties; } /** * Gets the string representation * of any passed argument. * * @param mixed $argument * @param int $depth * * @return mixed */ private static function formatArgument($argument, $depth = 0) { if ($argument instanceof MatcherInterface) { return (string) $argument; } if (\is_object($argument)) { return 'object(' . \get_class($argument) . ')'; } if (\is_int($argument) || \is_float($argument)) { return $argument; } if (\is_array($argument)) { if ($depth === 1) { $argument = '[...]'; } else { $sample = []; foreach ($argument as $key => $value) { $key = \is_int($key) ? $key : \sprintf("'%s'", $key); $value = self::formatArgument($value, $depth + 1); $sample[] = \sprintf('%s => %s', $key, $value); } $argument = '[' . \implode(', ', $sample) . ']'; } return (\strlen($argument) > 1000) ? \substr($argument, 0, 1000) . '...]' : $argument; } if (\is_bool($argument)) { return $argument ? 'true' : 'false'; } if (\is_resource($argument)) { return 'resource(...)'; } if ($argument === null) { return 'NULL'; } return "'" . $argument . "'"; } /** * Gets a specific demeter mock from the ones kept by the container. * * @template TMock of object * * @param class-string $demeterMockKey * * @return null|(LegacyMockInterface&MockInterface&TMock) */ private static function getExistingDemeterMock(Container $container, $demeterMockKey) { return $container->getMocks()[$demeterMockKey] ?? null; } /** * Gets a new demeter configured * mock from the container. * * @param string $parent * @param string $method * * @return LegacyMockInterface&MockInterface */ private static function getNewDemeterMock(Container $container, $parent, $method, ExpectationInterface $exp) { $newMockName = 'demeter_' . \md5($parent) . '_' . $method; $parRef = null; $parentMock = $exp->getMock(); if ($parentMock !== null) { $parRef = new ReflectionObject($parentMock); } if ($parRef instanceof ReflectionObject && $parRef->hasMethod($method)) { $parRefMethod = $parRef->getMethod($method); $parRefMethodRetType = Reflector::getReturnType($parRefMethod, true); if ($parRefMethodRetType !== null) { $returnTypes = \explode('|', $parRefMethodRetType); $filteredReturnTypes = array_filter($returnTypes, static function (string $type): bool { return ! Reflector::isReservedWord($type); }); if ($filteredReturnTypes !== []) { $nameBuilder = new MockNameBuilder(); $nameBuilder->addPart('\\' . $newMockName); $mock = self::namedMock( $nameBuilder->build(), ...$filteredReturnTypes ); $exp->andReturn($mock); return $mock; } } } $mock = $container->mock($newMockName); $exp->andReturn($mock); return $mock; } /** * Checks if the passed array representing a demeter * chain with the method names is empty. * * @return bool */ private static function noMoreElementsInChain(array $methodNames) { return $methodNames === []; } /** * Utility function to turn public properties and public get* and is* method values into an array. * * @param object $object * @param int $nesting * * @return array */ private static function objectToArray($object, $nesting = 3) { if ($nesting === 0) { return ['...']; } $defaultFormatter = static function ($object, $nesting) { return [ 'properties' => self::extractInstancePublicProperties($object, $nesting), ]; }; $class = \get_class($object); $formatter = self::getConfiguration()->getObjectFormatter($class, $defaultFormatter); $array = [ 'class' => $class, 'identity' => '#' . \md5(\spl_object_hash($object)), ]; return \array_merge($array, $formatter($object, $nesting)); } } PKZ,mockery/library/helpers.phpnuW+A|TMock|Closure(LegacyMockInterface&MockInterface&TMock):LegacyMockInterface&MockInterface&TMock|array> $args * * @return LegacyMockInterface&MockInterface&TMock */ function mock(...$args) { return Mockery::mock(...$args); } } if (! \function_exists('spy')) { /** * @template TSpy of object * * @param array|TSpy|Closure(LegacyMockInterface&MockInterface&TSpy):LegacyMockInterface&MockInterface&TSpy|array> $args * * @return LegacyMockInterface&MockInterface&TSpy */ function spy(...$args) { return Mockery::spy(...$args); } } if (! \function_exists('namedMock')) { /** * @template TNamedMock of object * * @param array|TNamedMock|array> $args * * @return LegacyMockInterface&MockInterface&TNamedMock */ function namedMock(...$args) { return Mockery::namedMock(...$args); } } if (! \function_exists('anyArgs')) { function anyArgs(): AnyArgs { return new AnyArgs(); } } if (! \function_exists('andAnyOtherArgs')) { function andAnyOtherArgs(): AndAnyOtherArgs { return new AndAnyOtherArgs(); } } if (! \function_exists('andAnyOthers')) { function andAnyOthers(): AndAnyOtherArgs { return new AndAnyOtherArgs(); } } PKZJ(""mockery/docs/conf.pynuW+A# -*- coding: utf-8 -*- # # Mockery Docs documentation build configuration file, created by # sphinx-quickstart on Mon Mar 3 14:04:26 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.todo', 'sphinx_rtd_theme', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Mockery Docs' copyright = u'Pádraic Brady, Dave Marshall and contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.6' # The full version, including alpha/beta/rc tags. release = '1.6.x' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'MockeryDocsdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'MockeryDocs.tex', u'Mockery Docs Documentation', u'Pádraic Brady, Dave Marshall, Wouter, Graham Campbell', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'mockerydocs', u'Mockery Docs Documentation', [u'Pádraic Brady, Dave Marshall, Wouter, Graham Campbell'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'MockeryDocs', u'Mockery Docs Documentation', u'Pádraic Brady, Dave Marshall, Wouter, Graham Campbell', 'MockeryDocs', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False #on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] print(sphinx_rtd_theme.get_html_theme_path()) # load PhpLexer from sphinx.highlighting import lexers from pygments.lexers.web import PhpLexer # enable highlighting for PHP code not between by default lexers['php'] = PhpLexer(startinline=True) lexers['php-annotations'] = PhpLexer(startinline=True) PKZmockery/docs/_static/.gitkeepnuW+APKZ)-M  mockery/docs/requirements.txtnuW+Aalabaster==0.7.16 Babel==2.14.0 certifi==2024.2.2 charset-normalizer==3.3.2 docutils==0.20.1 idna==3.7 imagesize==1.4.1 Jinja2==3.1.4 MarkupSafe==2.1.5 packaging==24.0 Pygments==2.17.2 requests==2.31.0 setuptools==69.2.0 snowballstemmer==2.2.0 Sphinx==7.3.7 sphinx-rtd-theme==2.0.0 sphinxcontrib-applehelp==1.0.8 sphinxcontrib-devhelp==1.0.6 sphinxcontrib-htmlhelp==2.0.5 sphinxcontrib-jquery==4.1 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.7 sphinxcontrib-serializinghtml==1.1.10 urllib3==2.2.1 wheel==0.43.0 PKZJXmockery/docs/.gitignorenuW+A_build PKZY~~mockery/docs/MakefilenuW+A# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/MockeryDocs.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/MockeryDocs.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/MockeryDocs" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/MockeryDocs" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." PKZ;g*mockery/docs/cookbook/big_parent_class.rstnuW+A.. index:: single: Cookbook; Big Parent Class Big Parent Class ================ In some application code, especially older legacy code, we can come across some classes that extend a "big parent class" - a parent class that knows and does too much: .. code-block:: php class BigParentClass { public function doesEverything() { // sets up database connections // writes to log files } } class ChildClass extends BigParentClass { public function doesOneThing() { // but calls on BigParentClass methods $result = $this->doesEverything(); // does something with $result return $result; } } We want to test our ``ChildClass`` and its ``doesOneThing`` method, but the problem is that it calls on ``BigParentClass::doesEverything()``. One way to handle this would be to mock out **all** of the dependencies ``BigParentClass`` has and needs, and then finally actually test our ``doesOneThing`` method. It's an awful lot of work to do that. What we can do, is to do something... unconventional. We can create a runtime partial test double of the ``ChildClass`` itself and mock only the parent's ``doesEverything()`` method: .. code-block:: php $childClass = \Mockery::mock('ChildClass')->makePartial(); $childClass->shouldReceive('doesEverything') ->andReturn('some result from parent'); $childClass->doesOneThing(); // string("some result from parent"); With this approach we mock out only the ``doesEverything()`` method, and all the unmocked methods are called on the actual ``ChildClass`` instance. PKZIi==4mockery/docs/cookbook/mocking_class_within_class.rstnuW+A.. index:: single: Cookbook; Mocking class within class .. _mocking-class-within-class: Mocking class within class ========================== Imagine a case where you need to create an instance of a class and use it within the same method: .. code-block:: php // Point.php setPoint($x1, $y1); $b = new Point(); $b->setPoint($x2, $y1); $c = new Point(); $c->setPoint($x2, $y2); $d = new Point(); $d->setPoint($x1, $y2); $this->draw([$a, $b, $c, $d]); } public function draw($points) { echo "Do something with the points"; } } And that you want to test that a logic in ``Rectangle->create()`` calls properly each used thing - in this case calls ``Point->setPoint()``, but ``Rectangle->draw()`` does some graphical stuff that you want to avoid calling. You set the mocks for ``App\Point`` and ``App\Rectangle``: .. code-block:: php shouldReceive("setPoint")->andThrow(Exception::class); $rect = Mockery::mock("App\Rectangle")->makePartial(); $rect->shouldReceive("draw"); $rect->create(0, 0, 100, 100); // does not throw exception Mockery::close(); } } and the test does not work. Why? The mocking relies on the class not being present yet, but the class is autoloaded therefore the mock alone for ``App\Point`` is useless which you can see with ``echo`` being executed. Mocks however work for the first class in the order of loading i.e. ``App\Rectangle``, which loads the ``App\Point`` class. In more complex example that would be a single point that initiates the whole loading (``use Class``) such as:: A // main loading initiator |- B // another loading initiator | |-E | +-G | |- C // another loading initiator | +-F | +- D That basically means that the loading prevents mocking and for each such a loading initiator there needs to be implemented a workaround. Overloading is one approach, however it pollutes the global state. In this case we try to completely avoid the global state pollution with custom ``new Class()`` behavior per loading initiator and that can be mocked easily in few critical places. That being said, although we can't stop loading, we can return mocks. Let's look at ``Rectangle->create()`` method: .. code-block:: php class Rectangle { public function newPoint() { return new Point(); } public function create($x1, $y1, $x2, $y2) { $a = $this->newPoint(); $a->setPoint($x1, $y1); ... } ... } We create a custom function to encapsulate ``new`` keyword that would otherwise just use the autoloaded class ``App\Point`` and in our test we mock that function so that it returns our mock: .. code-block:: php shouldReceive("setPoint")->andThrow(Exception::class); $rect = Mockery::mock("App\Rectangle")->makePartial(); $rect->shouldReceive("draw"); // pass the App\Point mock into App\Rectangle as an alternative // to using new App\Point() in-place. $rect->shouldReceive("newPoint")->andReturn($point); $this->expectException(Exception::class); $rect->create(0, 0, 100, 100); Mockery::close(); } } If we run this test now, it should pass. For more complex cases we'd find the next loader in the program flow and proceed with wrapping and passing mock instances with predefined behavior into already existing classes. PKZ:!mockery/docs/cookbook/map.rst.incnuW+A* :doc:`/cookbook/default_expectations` * :doc:`/cookbook/detecting_mock_objects` * :doc:`/cookbook/not_calling_the_constructor` * :doc:`/cookbook/mocking_hard_dependencies` * :doc:`/cookbook/class_constants` * :doc:`/cookbook/big_parent_class` * :doc:`/cookbook/mockery_on` PKZ=b0mockery/docs/cookbook/detecting_mock_objects.rstnuW+A.. index:: single: Cookbook; Detecting Mock Objects Detecting Mock Objects ====================== Users may find it useful to check whether a given object is a real object or a simulated Mock Object. All Mockery mocks implement the ``\Mockery\MockInterface`` interface which can be used in a type check. .. code-block:: php assert($mightBeMocked instanceof \Mockery\MockInterface); PKZrxx3mockery/docs/cookbook/mocking_hard_dependencies.rstnuW+A.. index:: single: Cookbook; Mocking Hard Dependencies Mocking Hard Dependencies (new Keyword) ======================================= One prerequisite to mock hard dependencies is that the code we are trying to test uses autoloading. Let's take the following code for an example: .. code-block:: php sendSomething($param); return $externalService->getSomething(); } } The way we can test this without doing any changes to the code itself is by creating :doc:`instance mocks ` by using the ``overload`` prefix. .. code-block:: php shouldReceive('sendSomething') ->once() ->with($param); $externalMock->shouldReceive('getSomething') ->once() ->andReturn('Tested!'); $service = new \App\Service(); $result = $service->callExternalService($param); $this->assertSame('Tested!', $result); } } If we run this test now, it should pass. Mockery does its job and our ``App\Service`` will use the mocked external service instead of the real one. The problem with this is when we want to, for example, test the ``App\Service\External`` itself, or if we use that class somewhere else in our tests. When Mockery overloads a class, because of how PHP works with files, that overloaded class file must not be included otherwise Mockery will throw a "class already exists" exception. This is where autoloading kicks in and makes our job a lot easier. To make this possible, we'll tell PHPUnit to run the tests that have overloaded classes in separate processes and to not preserve global state. That way we'll avoid having the overloaded class included more than once. Of course this has its downsides as these tests will run slower. Our test example from above now becomes: .. code-block:: php shouldReceive('sendSomething') ->once() ->with($param); $externalMock->shouldReceive('getSomething') ->once() ->andReturn('Tested!'); $service = new \App\Service(); $result = $service->callExternalService($param); $this->assertSame('Tested!', $result); } } Testing the constructor arguments of hard Dependencies ------------------------------------------------------ Sometimes we might want to ensure that the hard dependency is instantiated with particular arguments. With overloaded mocks, we can set up expectations on the constructor. .. code-block:: php allows('sendSomething'); $externalMock->shouldReceive('__construct') ->once() ->with(5); $service = new \App\Service(); $result = $service->callExternalService($param); } } .. note:: For more straightforward and single-process tests oriented way check :ref:`mocking-class-within-class`. .. note:: This cookbook entry is an adaption of the blog post titled `"Mocking hard dependencies with Mockery" `_, published by Robert Basic on his blog. PKZ?8E $mockery/docs/cookbook/mockery_on.rstnuW+A.. index:: single: Cookbook; Complex Argument Matching With Mockery::on Complex Argument Matching With Mockery::on ========================================== When we need to do a more complex argument matching for an expected method call, the ``\Mockery::on()`` matcher comes in really handy. It accepts a closure as an argument and that closure in turn receives the argument passed in to the method, when called. If the closure returns ``true``, Mockery will consider that the argument has passed the expectation. If the closure returns ``false``, or a "falsey" value, the expectation will not pass. The ``\Mockery::on()`` matcher can be used in various scenarios — validating an array argument based on multiple keys and values, complex string matching... Say, for example, we have the following code. It doesn't do much; publishes a post by setting the ``published`` flag in the database to ``1`` and sets the ``published_at`` to the current date and time: .. code-block:: php model = $model; } public function publishPost($id) { $saveData = [ 'post_id' => $id, 'published' => 1, 'published_at' => gmdate('Y-m-d H:i:s'), ]; $this->model->save($saveData); } } In a test we would mock the model and set some expectations on the call of the ``save()`` method: .. code-block:: php shouldReceive('save') ->once() ->with(\Mockery::on(function ($argument) use ($postId) { $postIdIsSet = isset($argument['post_id']) && $argument['post_id'] === $postId; $publishedFlagIsSet = isset($argument['published']) && $argument['published'] === 1; $publishedAtIsSet = isset($argument['published_at']); return $postIdIsSet && $publishedFlagIsSet && $publishedAtIsSet; })); $service = new \Service\Post($modelMock); $service->publishPost($postId); \Mockery::close(); The important part of the example is inside the closure we pass to the ``\Mockery::on()`` matcher. The ``$argument`` is actually the ``$saveData`` argument the ``save()`` method gets when it is called. We check for a couple of things in this argument: * the post ID is set, and is same as the post ID we passed in to the ``publishPost()`` method, * the ``published`` flag is set, and is ``1``, and * the ``published_at`` key is present. If any of these requirements is not satisfied, the closure will return ``false``, the method call expectation will not be met, and Mockery will throw a ``NoMatchingExpectationException``. .. note:: This cookbook entry is an adaption of the blog post titled `"Complex argument matching in Mockery" `_, published by Robert Basic on his blog. PKZIwmockery/docs/cookbook/index.rstnuW+ACookbook ======== .. toctree:: :hidden: default_expectations detecting_mock_objects not_calling_the_constructor mocking_hard_dependencies class_constants big_parent_class mockery_on mocking_class_within_class .. include:: map.rst.inc PKZ2hh5mockery/docs/cookbook/not_calling_the_constructor.rstnuW+A.. index:: single: Cookbook; Not Calling the Original Constructor Not Calling the Original Constructor ==================================== When creating generated partial test doubles, Mockery mocks out only the method which we specifically told it to. This means that the original constructor of the class we are mocking will be called. In some cases this is not a desired behavior, as the constructor might issue calls to other methods, or other object collaborators, and as such, can create undesired side-effects in the application's environment when running the tests. If this happens, we need to use runtime partial test doubles, as they don't call the original constructor. .. code-block:: php class MyClass { public function __construct() { echo "Original constructor called." . PHP_EOL; // Other side-effects can happen... } } // This will print "Original constructor called." $mock = \Mockery::mock('MyClass[foo]'); A better approach is to use runtime partial doubles: .. code-block:: php class MyClass { public function __construct() { echo "Original constructor called." . PHP_EOL; // Other side-effects can happen... } } // This will not print anything $mock = \Mockery::mock('MyClass')->makePartial(); $mock->shouldReceive('foo'); This is one of the reason why we don't recommend using generated partial test doubles, but if possible, always use the runtime partials. Read more about :ref:`creating-test-doubles-partial-test-doubles`. .. note:: The way generated partial test doubles work, is a BC break. If you use a really old version of Mockery, it might behave in a way that the constructor is not being called for these generated partials. In the case if you upgrade to a more recent version of Mockery, you'll probably have to change your tests to use runtime partials, instead of generated ones. This change was introduced in early 2013, so it is highly unlikely that you are using a Mockery from before that, so this should not be an issue. PKZ$wF.mockery/docs/cookbook/default_expectations.rstnuW+A.. index:: single: Cookbook; Default Mock Expectations Default Mock Expectations ========================= Often in unit testing, we end up with sets of tests which use the same object dependency over and over again. Rather than mocking this class/object within every single unit test (requiring a mountain of duplicate code), we can instead define reusable default mocks within the test case's ``setup()`` method. This even works where unit tests use varying expectations on the same or similar mock object. How this works, is that you can define mocks with default expectations. Then, in a later unit test, you can add or fine-tune expectations for that specific test. Any expectation can be set as a default using the ``byDefault()`` declaration. PKZ$DD)mockery/docs/cookbook/class_constants.rstnuW+A.. index:: single: Cookbook; Class Constants Class Constants =============== When creating a test double for a class, Mockery does not create stubs out of any class constants defined in the class we are mocking. Sometimes though, the non-existence of these class constants, setup of the test, and the application code itself, it can lead to undesired behavior, and even a PHP error: ``PHP Fatal error: Uncaught Error: Undefined class constant 'FOO' in ...`` While supporting class constants in Mockery would be possible, it does require an awful lot of work, for a small number of use cases. Named Mocks ----------- We can, however, deal with these constants in a way supported by Mockery - by using :ref:`creating-test-doubles-named-mocks`. A named mock is a test double that has a name of the class we want to mock, but under it is a stubbed out class that mimics the real class with canned responses. Lets look at the following made up, but not impossible scenario: .. code-block:: php class Fetcher { const SUCCESS = 0; const FAILURE = 1; public static function fetch() { // Fetcher gets something for us from somewhere... return self::SUCCESS; } } class MyClass { public function doFetching() { $response = Fetcher::fetch(); if ($response == Fetcher::SUCCESS) { echo "Thanks!" . PHP_EOL; } else { echo "Try again!" . PHP_EOL; } } } Our ``MyClass`` calls a ``Fetcher`` that fetches some resource from somewhere - maybe it downloads a file from a remote web service. Our ``MyClass`` prints out a response message depending on the response from the ``Fetcher::fetch()`` call. When testing ``MyClass`` we don't really want ``Fetcher`` to go and download random stuff from the internet every time we run our test suite. So we mock it out: .. code-block:: php // Using alias: because fetch is called statically! \Mockery::mock('alias:Fetcher') ->shouldReceive('fetch') ->andReturn(0); $myClass = new MyClass(); $myClass->doFetching(); If we run this, our test will error out with a nasty ``PHP Fatal error: Uncaught Error: Undefined class constant 'SUCCESS' in ..``. Here's how a ``namedMock()`` can help us in a situation like this. We create a stub for the ``Fetcher`` class, stubbing out the class constants, and then use ``namedMock()`` to create a mock named ``Fetcher`` based on our stub: .. code-block:: php class FetcherStub { const SUCCESS = 0; const FAILURE = 1; } \Mockery::namedMock('Fetcher', 'FetcherStub') ->shouldReceive('fetch') ->andReturn(0); $myClass = new MyClass(); $myClass->doFetching(); This works because under the hood, Mockery creates a class called ``Fetcher`` that extends ``FetcherStub``. The same approach will work even if ``Fetcher::fetch()`` is not a static dependency: .. code-block:: php class Fetcher { const SUCCESS = 0; const FAILURE = 1; public function fetch() { // Fetcher gets something for us from somewhere... return self::SUCCESS; } } class MyClass { public function doFetching($fetcher) { $response = $fetcher->fetch(); if ($response == Fetcher::SUCCESS) { echo "Thanks!" . PHP_EOL; } else { echo "Try again!" . PHP_EOL; } } } And the test will have something like this: .. code-block:: php class FetcherStub { const SUCCESS = 0; const FAILURE = 1; } $mock = \Mockery::mock('Fetcher', 'FetcherStub') $mock->shouldReceive('fetch') ->andReturn(0); $myClass = new MyClass(); $myClass->doFetching($mock); Constants Map ------------- Another way of mocking class constants can be with the use of the constants map configuration. Given a class with constants: .. code-block:: php class Fetcher { const SUCCESS = 0; const FAILURE = 1; public function fetch() { // Fetcher gets something for us from somewhere... return self::SUCCESS; } } It can be mocked with: .. code-block:: php \Mockery::getConfiguration()->setConstantsMap([ 'Fetcher' => [ 'SUCCESS' => 'success', 'FAILURE' => 'fail', ] ]); $mock = \Mockery::mock('Fetcher'); var_dump($mock::SUCCESS); // (string) 'success' var_dump($mock::FAILURE); // (string) 'fail' PKZ8n"Y3mockery/docs/reference/public_static_properties.rstnuW+A.. index:: single: Mocking; Public Static Methods Mocking Public Static Methods ============================= Static methods are not called on real objects, so normal mock objects can't mock them. Mockery supports class aliased mocks, mocks representing a class name which would normally be loaded (via autoloading or a require statement) in the system under test. These aliases block that loading (unless via a require statement - so please use autoloading!) and allow Mockery to intercept static method calls and add expectations for them. See the :ref:`creating-test-doubles-aliasing` section for more information on creating aliased mocks, for the purpose of mocking public static methods. PKZעVM M <mockery/docs/reference/alternative_should_receive_syntax.rstnuW+A.. index:: single: Alternative shouldReceive Syntax Alternative shouldReceive Syntax ================================ As of Mockery 1.0.0, we support calling methods as we would call any PHP method, and not as string arguments to Mockery ``should*`` methods. The two Mockery methods that enable this are ``allows()`` and ``expects()``. Allows ------ We use ``allows()`` when we create stubs for methods that return a predefined return value, but for these method stubs we don't care how many times, or if at all, were they called. .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->allows([ 'name_of_method_1' => 'return value', 'name_of_method_2' => 'return value', ]); This is equivalent with the following ``shouldReceive`` syntax: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive([ 'name_of_method_1' => 'return value', 'name_of_method_2' => 'return value', ]); Note that with this format, we also tell Mockery that we don't care about the arguments to the stubbed methods. If we do care about the arguments, we would do it like so: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->allows() ->name_of_method_1($arg1) ->andReturn('return value'); This is equivalent with the following ``shouldReceive`` syntax: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method_1') ->with($arg1) ->andReturn('return value'); Expects ------- We use ``expects()`` when we want to verify that a particular method was called: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->expects() ->name_of_method_1($arg1) ->andReturn('return value'); This is equivalent with the following ``shouldReceive`` syntax: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method_1') ->once() ->with($arg1) ->andReturn('return value'); By default ``expects()`` sets up an expectation that the method should be called once and once only. If we expect more than one call to the method, we can change that expectation: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->expects() ->name_of_method_1($arg1) ->twice() ->andReturn('return value'); PKZ4~8~80mockery/docs/reference/creating_test_doubles.rstnuW+A.. index:: single: Reference; Creating Test Doubles Creating Test Doubles ===================== Mockery's main goal is to help us create test doubles. It can create stubs, mocks, and spies. Stubs and mocks are created the same. The difference between the two is that a stub only returns a preset result when called, while a mock needs to have expectations set on the method calls it expects to receive. Spies are a type of test doubles that keep track of the calls they received, and allow us to inspect these calls after the fact. When creating a test double object, we can pass in an identifier as a name for our test double. If we pass it no identifier, the test double name will be unknown. Furthermore, the identifier does not have to be a class name. It is a good practice, and our recommendation, to always name the test doubles with the same name as the underlying class we are creating test doubles for. If the identifier we use for our test double is a name of an existing class, the test double will inherit the type of the class (via inheritance), i.e. the mock object will pass type hints or ``instanceof`` evaluations for the existing class. This is useful when a test double must be of a specific type, to satisfy the expectations our code has. Stubs and mocks --------------- Stubs and mocks are created by calling the ``\Mockery::mock()`` method. The following example shows how to create a stub, or a mock, object named "foo": .. code-block:: php $mock = \Mockery::mock('foo'); The mock object created like this is the loosest form of mocks possible, and is an instance of ``\Mockery\MockInterface``. .. note:: All test doubles created with Mockery are an instance of ``\Mockery\MockInterface``, regardless are they a stub, mock or a spy. To create a stub or a mock object with no name, we can call the ``mock()`` method with no parameters: .. code-block:: php $mock = \Mockery::mock(); As we stated earlier, we don't recommend creating stub or mock objects without a name. Classes, abstracts, interfaces ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The recommended way to create a stub or a mock object is by using a name of an existing class we want to create a test double of: .. code-block:: php $mock = \Mockery::mock('MyClass'); This stub or mock object will have the type of ``MyClass``, through inheritance. Stub or mock objects can be based on any concrete class, abstract class or even an interface. The primary purpose is to ensure the mock object inherits a specific type for type hinting. .. code-block:: php $mock = \Mockery::mock('MyInterface'); This stub or mock object will implement the ``MyInterface`` interface. .. note:: Classes marked final, or classes that have methods marked final cannot be mocked fully. Mockery supports creating partial mocks for these cases. Partial mocks will be explained later in the documentation. Mockery also supports creating stub or mock objects based on a single existing class, which must implement one or more interfaces. We can do this by providing a comma-separated list of the class and interfaces as the first argument to the ``\Mockery::mock()`` method: .. code-block:: php $mock = \Mockery::mock('MyClass, MyInterface, OtherInterface'); This stub or mock object will now be of type ``MyClass`` and implement the ``MyInterface`` and ``OtherInterface`` interfaces. .. note:: The class name doesn't need to be the first member of the list but it's a friendly convention to use for readability. We can tell a mock to implement the desired interfaces by passing the list of interfaces as the second argument: .. code-block:: php $mock = \Mockery::mock('MyClass', 'MyInterface, OtherInterface'); For all intents and purposes, this is the same as the previous example. Spies ----- The third type of test doubles Mockery supports are spies. The main difference between spies and mock objects is that with spies we verify the calls made against our test double after the calls were made. We would use a spy when we don't necessarily care about all of the calls that are going to be made to an object. A spy will return ``null`` for all method calls it receives. It is not possible to tell a spy what will be the return value of a method call. If we do that, then we would deal with a mock object, and not with a spy. We create a spy by calling the ``\Mockery::spy()`` method: .. code-block:: php $spy = \Mockery::spy('MyClass'); Just as with stubs or mocks, we can tell Mockery to base a spy on any concrete or abstract class, or to implement any number of interfaces: .. code-block:: php $spy = \Mockery::spy('MyClass, MyInterface, OtherInterface'); This spy will now be of type ``MyClass`` and implement the ``MyInterface`` and ``OtherInterface`` interfaces. .. note:: The ``\Mockery::spy()`` method call is actually a shorthand for calling ``\Mockery::mock()->shouldIgnoreMissing()``. The ``shouldIgnoreMissing`` method is a "behaviour modifier". We'll discuss them a bit later. Mocks vs. Spies --------------- Let's try and illustrate the difference between mocks and spies with the following example: .. code-block:: php $mock = \Mockery::mock('MyClass'); $spy = \Mockery::spy('MyClass'); $mock->shouldReceive('foo')->andReturn(42); $mockResult = $mock->foo(); $spyResult = $spy->foo(); $spy->shouldHaveReceived()->foo(); var_dump($mockResult); // int(42) var_dump($spyResult); // null As we can see from this example, with a mock object we set the call expectations before the call itself, and we get the return result we expect it to return. With a spy object on the other hand, we verify the call has happened after the fact. The return result of a method call against a spy is always ``null``. We also have a dedicated chapter to :doc:`spies` only. .. _creating-test-doubles-partial-test-doubles: Partial Test Doubles -------------------- Partial doubles are useful when we want to stub out, set expectations for, or spy on *some* methods of a class, but run the actual code for other methods. We differentiate between three types of partial test doubles: * runtime partial test doubles, * generated partial test doubles, and * proxied partial test doubles. Runtime partial test doubles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ What we call a runtime partial, involves creating a test double and then telling it to make itself partial. Any method calls that the double hasn't been told to allow or expect, will act as they would on a normal instance of the object. .. code-block:: php class Foo { function foo() { return 123; } function bar() { return $this->foo(); } } $foo = mock(Foo::class)->makePartial(); $foo->foo(); // int(123); We can then tell the test double to allow or expect calls as with any other Mockery double. .. code-block:: php $foo->shouldReceive('foo')->andReturn(456); $foo->bar(); // int(456) See the cookbook entry on :doc:`../cookbook/big_parent_class` for an example usage of runtime partial test doubles. Generated partial test doubles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The second type of partial double we can create is what we call a generated partial. With generated partials, we specifically tell Mockery which methods we want to be able to allow or expect calls to. All other methods will run the actual code *directly*, so stubs and expectations on these methods will not work. .. code-block:: php class Foo { function foo() { return 123; } function bar() { return $this->foo(); } } $foo = mock("Foo[foo]"); $foo->foo(); // error, no expectation set $foo->shouldReceive('foo')->andReturn(456); $foo->foo(); // int(456) // setting an expectation for this has no effect $foo->shouldReceive('bar')->andReturn(999); $foo->bar(); // int(456) It's also possible to specify explicitly which methods to run directly using the `!method` syntax: .. code-block:: php class Foo { function foo() { return 123; } function bar() { return $this->foo(); } } $foo = mock("Foo[!foo]"); $foo->foo(); // int(123) $foo->bar(); // error, no expectation set .. note:: Even though we support generated partial test doubles, we do not recommend using them. One of the reasons why is because a generated partial will call the original constructor of the mocked class. This can have unwanted side-effects during testing application code. See :doc:`../cookbook/not_calling_the_constructor` for more details. Proxied partial test doubles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A proxied partial mock is a partial of last resort. We may encounter a class which is simply not capable of being mocked because it has been marked as final. Similarly, we may find a class with methods marked as final. In such a scenario, we cannot simply extend the class and override methods to mock - we need to get creative. .. code-block:: php $mock = \Mockery::mock(new MyClass); Yes, the new mock is a Proxy. It intercepts calls and reroutes them to the proxied object (which we construct and pass in) for methods which are not subject to any expectations. Indirectly, this allows us to mock methods marked final since the Proxy is not subject to those limitations. The tradeoff should be obvious - a proxied partial will fail any typehint checks for the class being mocked since it cannot extend that class. .. _creating-test-doubles-aliasing: Aliasing -------- Prefixing the valid name of a class (which is NOT currently loaded) with "alias:" will generate an "alias mock". Alias mocks create a class alias with the given classname to stdClass and are generally used to enable the mocking of public static methods. Expectations set on the new mock object which refer to static methods will be used by all static calls to this class. .. code-block:: php $mock = \Mockery::mock('alias:MyClass'); .. note:: Even though aliasing classes is supported, we do not recommend it. Overloading ----------- Prefixing the valid name of a class (which is NOT currently loaded) with "overload:" will generate an alias mock (as with "alias:") except that created new instances of that class will import any expectations set on the origin mock (``$mock``). The origin mock is never verified since it's used an expectation store for new instances. For this purpose we use the term "instance mock" to differentiate it from the simpler "alias mock". In other words, an instance mock will "intercept" when a new instance of the mocked class is created, then the mock will be used instead. This is useful especially when mocking hard dependencies which will be discussed later. .. code-block:: php $mock = \Mockery::mock('overload:MyClass'); .. note:: Using alias/instance mocks across more than one test will generate a fatal error since we can't have two classes of the same name. To avoid this, run each test of this kind in a separate PHP process (which is supported out of the box by both PHPUnit and PHPT). .. _creating-test-doubles-named-mocks: Named Mocks ----------- The ``namedMock()`` method will generate a class called by the first argument, so in this example ``MyClassName``. The rest of the arguments are treated in the same way as the ``mock`` method: .. code-block:: php $mock = \Mockery::namedMock('MyClassName', 'DateTime'); This example would create a class called ``MyClassName`` that extends ``DateTime``. Named mocks are quite an edge case, but they can be useful when code depends on the ``__CLASS__`` magic constant, or when we need two derivatives of an abstract type, that are actually different classes. See the cookbook entry on :doc:`../cookbook/class_constants` for an example usage of named mocks. .. note:: We can only create a named mock once, any subsequent calls to ``namedMock``, with different arguments are likely to cause exceptions. .. _creating-test-doubles-constructor-arguments: Constructor Arguments --------------------- Sometimes the mocked class has required constructor arguments. We can pass these to Mockery as an indexed array, as the 2nd argument: .. code-block:: php $mock = \Mockery::mock('MyClass', [$constructorArg1, $constructorArg2]); or if we need the ``MyClass`` to implement an interface as well, as the 3rd argument: .. code-block:: php $mock = \Mockery::mock('MyClass', 'MyInterface', [$constructorArg1, $constructorArg2]); Mockery now knows to pass in ``$constructorArg1`` and ``$constructorArg2`` as arguments to the constructor. .. _creating-test-doubles-behavior-modifiers: Behavior Modifiers ------------------ When creating a mock object, we may wish to use some commonly preferred behaviours that are not the default in Mockery. The use of the ``shouldIgnoreMissing()`` behaviour modifier will label this mock object as a Passive Mock: .. code-block:: php \Mockery::mock('MyClass')->shouldIgnoreMissing(); In such a mock object, calls to methods which are not covered by expectations will return ``null`` instead of the usual error about there being no expectation matching the call. On PHP >= 7.0.0, methods with missing expectations that have a return type will return either a mock of the object (if return type is a class) or a "falsy" primitive value, e.g. empty string, empty array, zero for ints and floats, false for bools, or empty closures. On PHP >= 7.1.0, methods with missing expectations and nullable return type will return null. We can optionally prefer to return an object of type ``\Mockery\Undefined`` (i.e. a ``null`` object) (which was the 0.7.2 behaviour) by using an additional modifier: .. code-block:: php \Mockery::mock('MyClass')->shouldIgnoreMissing()->asUndefined(); The returned object is nothing more than a placeholder so if, by some act of fate, it's erroneously used somewhere it shouldn't, it will likely not pass a logic check. We have encountered the ``makePartial()`` method before, as it is the method we use to create runtime partial test doubles: .. code-block:: php \Mockery::mock('MyClass')->makePartial(); This form of mock object will defer all methods not subject to an expectation to the parent class of the mock, i.e. ``MyClass``. Whereas the previous ``shouldIgnoreMissing()`` returned ``null``, this behaviour simply calls the parent's matching method. PKZa%%+mockery/docs/reference/instance_mocking.rstnuW+A.. index:: single: Mocking; Instance Instance Mocking ================ Instance mocking means that a statement like: .. code-block:: php $obj = new \MyNamespace\Foo; ...will actually generate a mock object. This is done by replacing the real class with an instance mock (similar to an alias mock), as with mocking public methods. The alias will import its expectations from the original mock of that type (note that the original is never verified and should be ignored after its expectations are setup). This lets you intercept instantiation where you can't simply inject a replacement object. As before, this does not prevent a require statement from including the real class and triggering a fatal PHP error. It's intended for use where autoloading is the primary class loading mechanism. PKZ2>&&"mockery/docs/reference/map.rst.incnuW+A* :doc:`/reference/creating_test_doubles` * :doc:`/reference/expectations` * :doc:`/reference/argument_validation` * :doc:`/reference/alternative_should_receive_syntax` * :doc:`/reference/spies` * :doc:`/reference/partial_mocks` * :doc:`/reference/protected_methods` * :doc:`/reference/public_properties` * :doc:`/reference/public_static_properties` * :doc:`/reference/pass_by_reference_behaviours` * :doc:`/reference/demeter_chains` * :doc:`/reference/final_methods_classes` * :doc:`/reference/magic_methods` * :doc:`/reference/phpunit_integration` PKZA. .mockery/docs/reference/phpunit_integration.rstnuW+A.. index:: single: PHPUnit Integration PHPUnit Integration =================== Mockery was designed as a simple-to-use *standalone* mock object framework, so its need for integration with any testing framework is entirely optional. To integrate Mockery, we need to define a ``tearDown()`` method for our tests containing the following (we may use a shorter ``\Mockery`` namespace alias): .. code-block:: php public function tearDown() { \Mockery::close(); } This static call cleans up the Mockery container used by the current test, and run any verification tasks needed for our expectations. For some added brevity when it comes to using Mockery, we can also explicitly use the Mockery namespace with a shorter alias. For example: .. code-block:: php use \Mockery as m; class SimpleTest extends \PHPUnit\Framework\TestCase { public function testSimpleMock() { $mock = m::mock('simplemock'); $mock->shouldReceive('foo')->with(5, m::any())->once()->andReturn(10); $this->assertEquals(10, $mock->foo(5)); } public function tearDown() { m::close(); } } Mockery ships with an autoloader so we don't need to litter our tests with ``require_once()`` calls. To use it, ensure Mockery is on our ``include_path`` and add the following to our test suite's ``Bootstrap.php`` or ``TestHelper.php`` file: .. code-block:: php require_once 'Mockery/Loader.php'; require_once 'Hamcrest/Hamcrest.php'; $loader = new \Mockery\Loader; $loader->register(); If we are using Composer, we can simplify this to including the Composer generated autoloader file: .. code-block:: php require __DIR__ . '/../vendor/autoload.php'; // assuming vendor is one directory up .. caution:: Prior to Hamcrest 1.0.0, the ``Hamcrest.php`` file name had a small "h" (i.e. ``hamcrest.php``). If upgrading Hamcrest to 1.0.0 remember to check the file name is updated for all your projects.) To integrate Mockery into PHPUnit and avoid having to call the close method and have Mockery remove itself from code coverage reports, have your test case extends the ``\Mockery\Adapter\Phpunit\MockeryTestCase``: .. code-block:: php class MyTest extends \Mockery\Adapter\Phpunit\MockeryTestCase { } An alternative is to use the supplied trait: .. code-block:: php class MyTest extends \PHPUnit\Framework\TestCase { use \Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; } Extending ``MockeryTestCase`` or using the ``MockeryPHPUnitIntegration`` trait is **the recommended way** of integrating Mockery with PHPUnit, since Mockery 1.0.0. PHPUnit listener ---------------- Before the 1.0.0 release, Mockery provided a PHPUnit listener that would call ``Mockery::close()`` for us at the end of a test. This has changed significantly since the 1.0.0 version. Now, Mockery provides a PHPUnit listener that makes tests fail if ``Mockery::close()`` has not been called. It can help identify tests where we've forgotten to include the trait or extend the ``MockeryTestCase``. If we are using PHPUnit's XML configuration approach, we can include the following to load the ``TestListener``: .. code-block:: xml Make sure Composer's or Mockery's autoloader is present in the bootstrap file or we will need to also define a "file" attribute pointing to the file of the ``TestListener`` class. If we are creating the test suite programmatically we may add the listener like this: .. code-block:: php // Create the suite. $suite = new PHPUnit\Framework\TestSuite(); // Create the listener and add it to the suite. $result = new PHPUnit\Framework\TestResult(); $result->addListener(new \Mockery\Adapter\Phpunit\TestListener()); // Run the tests. $suite->run($result); .. caution:: PHPUnit provides a functionality that allows `tests to run in a separated process `_, to ensure better isolation. Mockery verifies the mocks expectations using the ``Mockery::close()`` method, and provides a PHPUnit listener, that automatically calls this method for us after every test. However, this listener is not called in the right process when using PHPUnit's process isolation, resulting in expectations that might not be respected, but without raising any ``Mockery\Exception``. To avoid this, we cannot rely on the supplied Mockery PHPUnit ``TestListener``, and we need to explicitly call ``Mockery::close``. The easiest solution to include this call in the ``tearDown()`` method, as explained previously. PKZbgg)mockery/docs/reference/demeter_chains.rstnuW+A.. index:: single: Mocking; Demeter Chains Mocking Demeter Chains And Fluent Interfaces ============================================ Both of these terms refer to the growing practice of invoking statements similar to: .. code-block:: php $object->foo()->bar()->zebra()->alpha()->selfDestruct(); The long chain of method calls isn't necessarily a bad thing, assuming they each link back to a local object the calling class knows. As a fun example, Mockery's long chains (after the first ``shouldReceive()`` method) all call to the same instance of ``\Mockery\Expectation``. However, sometimes this is not the case and the chain is constantly crossing object boundaries. In either case, mocking such a chain can be a horrible task. To make it easier Mockery supports demeter chain mocking. Essentially, we shortcut through the chain and return a defined value from the final call. For example, let's assume ``selfDestruct()`` returns the string "Ten!" to $object (an instance of ``CaptainsConsole``). Here's how we could mock it. .. code-block:: php $mock = \Mockery::mock('CaptainsConsole'); $mock->shouldReceive('foo->bar->zebra->alpha->selfDestruct')->andReturn('Ten!'); The above expectation can follow any previously seen format or expectation, except that the method name is simply the string of all expected chain calls separated by ``->``. Mockery will automatically setup the chain of expected calls with its final return values, regardless of whatever intermediary object might be used in the real implementation. Arguments to all members of the chain (except the final call) are ignored in this process. PKZ437(mockery/docs/reference/partial_mocks.rstnuW+A.. index:: single: Mocking; Partial Mocks Creating Partial Mocks ====================== Partial mocks are useful when we only need to mock several methods of an object leaving the remainder free to respond to calls normally (i.e. as implemented). Mockery implements three distinct strategies for creating partials. Each has specific advantages and disadvantages so which strategy we use will depend on our own preferences and the source code in need of mocking. We have previously talked a bit about :ref:`creating-test-doubles-partial-test-doubles`, but we'd like to expand on the subject a bit here. #. Runtime partial test doubles #. Generated partial test doubles #. Proxied Partial Mock Runtime partial test doubles ---------------------------- A runtime partial test double, also known as a passive partial mock, is a kind of a default state of being for a mocked object. .. code-block:: php $mock = \Mockery::mock('MyClass')->makePartial(); With a runtime partial, we assume that all methods will simply defer to the parent class (``MyClass``) original methods unless a method call matches a known expectation. If we have no matching expectation for a specific method call, that call is deferred to the class being mocked. Since the division between mocked and unmocked calls depends entirely on the expectations we define, there is no need to define which methods to mock in advance. See the cookbook entry on :doc:`../cookbook/big_parent_class` for an example usage of runtime partial test doubles. Generated Partial Test Doubles ------------------------------ A generated partial test double, also known as a traditional partial mock, defines ahead of time which methods of a class are to be mocked and which are to be left unmocked (i.e. callable as normal). The syntax for creating traditional mocks is: .. code-block:: php $mock = \Mockery::mock('MyClass[foo,bar]'); In the above example, the ``foo()`` and ``bar()`` methods of MyClass will be mocked but no other MyClass methods are touched. We will need to define expectations for the ``foo()`` and ``bar()`` methods to dictate their mocked behaviour. Don't forget that we can pass in constructor arguments since unmocked methods may rely on those! .. code-block:: php $mock = \Mockery::mock('MyNamespace\MyClass[foo]', array($arg1, $arg2)); See the :ref:`creating-test-doubles-constructor-arguments` section to read up on them. .. note:: Even though we support generated partial test doubles, we do not recommend using them. Proxied Partial Mock -------------------- A proxied partial mock is a partial of last resort. We may encounter a class which is simply not capable of being mocked because it has been marked as final. Similarly, we may find a class with methods marked as final. In such a scenario, we cannot simply extend the class and override methods to mock - we need to get creative. .. code-block:: php $mock = \Mockery::mock(new MyClass); Yes, the new mock is a Proxy. It intercepts calls and reroutes them to the proxied object (which we construct and pass in) for methods which are not subject to any expectations. Indirectly, this allows us to mock methods marked final since the Proxy is not subject to those limitations. The tradeoff should be obvious - a proxied partial will fail any typehint checks for the class being mocked since it cannot extend that class. Special Internal Cases ---------------------- All mock objects, with the exception of Proxied Partials, allows us to make any expectation call to the underlying real class method using the ``passthru()`` expectation call. This will return values from the real call and bypass any mocked return queue (which can simply be omitted obviously). There is a fourth kind of partial mock reserved for internal use. This is automatically generated when we attempt to mock a class containing methods marked final. Since we cannot override such methods, they are simply left unmocked. Typically, we don't need to worry about this but if we really really must mock a final method, the only possible means is through a Proxied Partial Mock. SplFileInfo, for example, is a common class subject to this form of automatic internal partial since it contains public final methods used internally. PKZQ55,mockery/docs/reference/public_properties.rstnuW+A.. index:: single: Mocking; Public Properties Mocking Public Properties ========================= Mockery allows us to mock properties in several ways. One way is that we can set a public property and its value on any mock object. The second is that we can use the expectation methods ``set()`` and ``andSet()`` to set property values if that expectation is ever met. You can read more about :ref:`expectations-setting-public-properties`. .. note:: In general, Mockery does not support mocking any magic methods since these are generally not considered a public API (and besides it is a bit difficult to differentiate them when you badly need them for mocking!). So please mock virtual properties (those relying on ``__get()`` and ``__set()``) as if they were actually declared on the class. PKZyd mockery/docs/reference/index.rstnuW+AReference ========= .. toctree:: :hidden: creating_test_doubles expectations argument_validation alternative_should_receive_syntax spies instance_mocking partial_mocks protected_methods public_properties public_static_properties pass_by_reference_behaviours demeter_chains final_methods_classes magic_methods phpunit_integration .. include:: map.rst.inc PKZDҼJ>J>'mockery/docs/reference/expectations.rstnuW+A.. index:: single: Expectations Expectation Declarations ======================== .. note:: In order for our expectations to work we MUST call ``Mockery::close()``, preferably in a callback method such as ``tearDown`` or ``_after`` (depending on whether or not we're integrating Mockery with another framework). This static call cleans up the Mockery container used by the current test, and run any verification tasks needed for our expectations. Once we have created a mock object, we'll often want to start defining how exactly it should behave (and how it should be called). This is where the Mockery expectation declarations take over. Declaring Method Call Expectations ---------------------------------- To tell our test double to expect a call for a method with a given name, we use the ``shouldReceive`` method: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method'); This is the starting expectation upon which all other expectations and constraints are appended. We can declare more than one method call to be expected: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method_1', 'name_of_method_2'); All of these will adopt any chained expectations or constraints. It is possible to declare the expectations for the method calls, along with their return values: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive([ 'name_of_method_1' => 'return value 1', 'name_of_method_2' => 'return value 2', ]); There's also a shorthand way of setting up method call expectations and their return values: .. code-block:: php $mock = \Mockery::mock('MyClass', ['name_of_method_1' => 'return value 1', 'name_of_method_2' => 'return value 2']); All of these will adopt any additional chained expectations or constraints. We can declare that a test double should not expect a call to the given method name: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldNotReceive('name_of_method'); This method is a convenience method for calling ``shouldReceive()->never()``. Declaring Method Argument Expectations -------------------------------------- For every method we declare expectation for, we can add constraints that the defined expectations apply only to the method calls that match the expected argument list: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->with($arg1, $arg2, ...); // or $mock->shouldReceive('name_of_method') ->withArgs([$arg1, $arg2, ...]); We can add a lot more flexibility to argument matching using the built in matcher classes (see later). For example, ``\Mockery::any()`` matches any argument passed to that position in the ``with()`` parameter list. Mockery also allows Hamcrest library matchers - for example, the Hamcrest function ``anything()`` is equivalent to ``\Mockery::any()``. It's important to note that this means all expectations attached only apply to the given method when it is called with these exact arguments: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo')->with('Hello'); $mock->foo('Goodbye'); // throws a NoMatchingExpectationException This allows for setting up differing expectations based on the arguments provided to expected calls. Argument matching with closures ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Instead of providing a built-in matcher for each argument, we can provide a closure that matches all passed arguments at once: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->withArgs(closure); The given closure receives all the arguments passed in the call to the expected method. In this way, this expectation only applies to method calls where passed arguments make the closure evaluate to true: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo')->withArgs(function ($arg) { if ($arg % 2 == 0) { return true; } return false; }); $mock->foo(4); // matches the expectation $mock->foo(3); // throws a NoMatchingExpectationException Argument matching with some of given values ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We can provide expected arguments that match passed arguments when mocked method is called. .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->withSomeOfArgs(arg1, arg2, arg3, ...); The given expected arguments order doesn't matter. Check if expected values are included or not, but type should be matched: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->withSomeOfArgs(1, 2); $mock->foo(1, 2, 3); // matches the expectation $mock->foo(3, 2, 1); // matches the expectation (passed order doesn't matter) $mock->foo('1', '2'); // throws a NoMatchingExpectationException (type should be matched) $mock->foo(3); // throws a NoMatchingExpectationException Any, or no arguments ^^^^^^^^^^^^^^^^^^^^ We can declare that the expectation matches a method call regardless of what arguments are passed: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->withAnyArgs(); This is set by default unless otherwise specified. We can declare that the expectation matches method calls with zero arguments: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->withNoArgs(); Declaring Return Value Expectations ----------------------------------- For mock objects, we can tell Mockery what return values to return from the expected method calls. For that we can use the ``andReturn()`` method: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturn($value); This sets a value to be returned from the expected method call. It is possible to set up expectation for multiple return values. By providing a sequence of return values, we tell Mockery what value to return on every subsequent call to the method: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturn($value1, $value2, ...) The first call will return ``$value1`` and the second call will return ``$value2``. If we call the method more times than the number of return values we declared, Mockery will return the final value for any subsequent method call: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo')->andReturn(1, 2, 3); $mock->foo(); // int(1) $mock->foo(); // int(2) $mock->foo(); // int(3) $mock->foo(); // int(3) The same can be achieved using the alternative syntax: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturnValues([$value1, $value2, ...]) It accepts a simple array instead of a list of parameters. The order of return is determined by the numerical index of the given array with the last array member being returned on all calls once previous return values are exhausted. The following two options are primarily for communication with test readers: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturnNull(); // or $mock->shouldReceive('name_of_method') ->andReturn([null]); They mark the mock object method call as returning ``null`` or nothing. Sometimes we want to calculate the return results of the method calls, based on the arguments passed to the method. We can do that with the ``andReturnUsing()`` method which accepts one or more closure: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturnUsing(closure, ...); Closures can be queued by passing them as extra parameters as for ``andReturn()``. Occasionally, it can be useful to echo back one of the arguments that a method is called with. In this case we can use the ``andReturnArg()`` method; the argument to be returned is specified by its index in the arguments list: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturnArg(1); This returns the second argument (index #1) from the list of arguments when the method is called. .. note:: We cannot currently mix ``andReturnUsing()`` or ``andReturnArg`` with ``andReturn()``. If we are mocking fluid interfaces, the following method will be helpful: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturnSelf(); It sets the return value to the mocked class name. Throwing Exceptions ------------------- We can tell the method of mock objects to throw exceptions: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andThrow(new Exception); It will throw the given ``Exception`` object when called. Rather than an object, we can pass in the ``Exception`` class, message and/or code to use when throwing an ``Exception`` from the mocked method: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andThrow('exception_name', 'message', 123456789); .. _expectations-setting-public-properties: Setting Public Properties ------------------------- Used with an expectation so that when a matching method is called, we can cause a mock object's public property to be set to a specified value, by using ``andSet()`` or ``set()``: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andSet($property, $value); // or $mock->shouldReceive('name_of_method') ->set($property, $value); In cases where we want to call the real method of the class that was mocked and return its result, the ``passthru()`` method tells the expectation to bypass a return queue: .. code-block:: php passthru() It allows expectation matching and call count validation to be applied against real methods while still calling the real class method with the expected arguments. Declaring Call Count Expectations --------------------------------- Besides setting expectations on the arguments of the method calls, and the return values of those same calls, we can set expectations on how many times should any method be called. When a call count expectation is not met, a ``\Mockery\Expectation\InvalidCountException`` will be thrown. .. note:: It is absolutely required to call ``\Mockery::close()`` at the end of our tests, for example in the ``tearDown()`` method of PHPUnit. Otherwise Mockery will not verify the calls made against our mock objects. We can declare that the expected method may be called zero or more times: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->zeroOrMoreTimes(); This is the default for all methods unless otherwise set. To tell Mockery to expect an exact number of calls to a method, we can use the following: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->times($n); where ``$n`` is the number of times the method should be called. A couple of most common cases got their shorthand methods. To declare that the expected method must be called one time only: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->once(); To declare that the expected method must be called two times: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->twice(); To declare that the expected method must never be called: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->never(); Call count modifiers ^^^^^^^^^^^^^^^^^^^^ The call count expectations can have modifiers set. If we want to tell Mockery the minimum number of times a method should be called, we use ``atLeast()``: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->atLeast() ->times(3); ``atLeast()->times(3)`` means the call must be called at least three times (given matching method args) but never less than three times. Similarly, we can tell Mockery the maximum number of times a method should be called, using ``atMost()``: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->atMost() ->times(3); ``atMost()->times(3)`` means the call must be called no more than three times. If the method gets no calls at all, the expectation will still be met. We can also set a range of call counts, using ``between()``: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->between($min, $max); This is actually identical to using ``atLeast()->times($min)->atMost()->times($max)`` but is provided as a shorthand. It may be followed by a ``times()`` call with no parameter to preserve the APIs natural language readability. Multiple Calls with Different Expectations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If a method is expected to get called multiple times with different arguments and/or return values we can simply repeat the expectations. The same of course also works if we expect multiple calls to different methods. .. code-block:: php $mock = \Mockery::mock('MyClass'); // Expectations for the 1st call $mock->shouldReceive('name_of_method') ->once() ->with('arg1') ->andReturn($value1) // 2nd call to same method ->shouldReceive('name_of_method') ->once() ->with('arg2') ->andReturn($value2) // final call to another method ->shouldReceive('other_method') ->once() ->with('other') ->andReturn($value_other); Expectation Declaration Utilities --------------------------------- Declares that this method is expected to be called in a specific order in relation to similarly marked methods. .. code-block:: php ordered() The order is dictated by the order in which this modifier is actually used when setting up mocks. Declares the method as belonging to an order group (which can be named or numbered). Methods within a group can be called in any order, but the ordered calls from outside the group are ordered in relation to the group: .. code-block:: php ordered(group) We can set up so that method1 is called before group1 which is in turn called before method2. When called prior to ``ordered()`` or ``ordered(group)``, it declares this ordering to apply across all mock objects (not just the current mock): .. code-block:: php globally() This allows for dictating order expectations across multiple mocks. The ``byDefault()`` marks an expectation as a default. Default expectations are applied unless a non-default expectation is created: .. code-block:: php byDefault() These later expectations immediately replace the previously defined default. This is useful so we can setup default mocks in our unit test ``setup()`` and later tweak them in specific tests as needed. Returns the current mock object from an expectation chain: .. code-block:: php getMock() Useful where we prefer to keep mock setups as a single statement, e.g.: .. code-block:: php $mock = \Mockery::mock('foo')->shouldReceive('foo')->andReturn(1)->getMock(); PKZZLig7mockery/docs/reference/pass_by_reference_behaviours.rstnuW+A.. index:: single: Pass-By-Reference Method Parameter Behaviour Preserving Pass-By-Reference Method Parameter Behaviour ======================================================= PHP Class method may accept parameters by reference. In this case, changes made to the parameter (a reference to the original variable passed to the method) are reflected in the original variable. An example: .. code-block:: php class Foo { public function bar(&$a) { $a++; } } $baz = 1; $foo = new Foo; $foo->bar($baz); echo $baz; // will echo the integer 2 In the example above, the variable ``$baz`` is passed by reference to ``Foo::bar()`` (notice the ``&`` symbol in front of the parameter?). Any change ``bar()`` makes to the parameter reference is reflected in the original variable, ``$baz``. Mockery handles references correctly for all methods where it can analyse the parameter (using ``Reflection``) to see if it is passed by reference. To mock how a reference is manipulated by the class method, we can use a closure argument matcher to manipulate it, i.e. ``\Mockery::on()`` - see the :ref:`argument-validation-complex-argument-validation` chapter. There is an exception for internal PHP classes where Mockery cannot analyse method parameters using ``Reflection`` (a limitation in PHP). To work around this, we can explicitly declare method parameters for an internal class using ``\Mockery\Configuration::setInternalClassMethodParamMap()``. Here's an example using ``MongoCollection::insert()``. ``MongoCollection`` is an internal class offered by the mongo extension from PECL. Its ``insert()`` method accepts an array of data as the first parameter, and an optional options array as the second parameter. The original data array is updated (i.e. when a ``insert()`` pass-by-reference parameter) to include a new ``_id`` field. We can mock this behaviour using a configured parameter map (to tell Mockery to expect a pass by reference parameter) and a ``Closure`` attached to the expected method parameter to be updated. Here's a PHPUnit unit test verifying that this pass-by-reference behaviour is preserved: .. code-block:: php public function testCanOverrideExpectedParametersOfInternalPHPClassesToPreserveRefs() { \Mockery::getConfiguration()->setInternalClassMethodParamMap( 'MongoCollection', 'insert', array('&$data', '$options = array()') ); $m = \Mockery::mock('MongoCollection'); $m->shouldReceive('insert')->with( \Mockery::on(function(&$data) { if (!is_array($data)) return false; $data['_id'] = 123; return true; }), \Mockery::any() ); $data = array('a'=>1,'b'=>2); $m->insert($data); $this->assertTrue(isset($data['_id'])); $this->assertEquals(123, $data['_id']); \Mockery::resetContainer(); } Protected Methods ----------------- When dealing with protected methods, and trying to preserve pass by reference behavior for them, a different approach is required. .. code-block:: php class Model { public function test(&$data) { return $this->doTest($data); } protected function doTest(&$data) { $data['something'] = 'wrong'; return $this; } } class Test extends \PHPUnit\Framework\TestCase { public function testModel() { $mock = \Mockery::mock('Model[test]')->shouldAllowMockingProtectedMethods(); $mock->shouldReceive('test') ->with(\Mockery::on(function(&$data) { $data['something'] = 'wrong'; return true; })); $data = array('foo' => 'bar'); $mock->test($data); $this->assertTrue(isset($data['something'])); $this->assertEquals('wrong', $data['something']); } } This is quite an edge case, so we need to change the original code a little bit, by creating a public method that will call our protected method, and then mock that, instead of the protected method. This new public method will act as a proxy to our protected method. PKZG,mockery/docs/reference/protected_methods.rstnuW+A.. index:: single: Mocking; Protected Methods Mocking Protected Methods ========================= By default, Mockery does not allow mocking protected methods. We do not recommend mocking protected methods, but there are cases when there is no other solution. For those cases we have the ``shouldAllowMockingProtectedMethods()`` method. It instructs Mockery to specifically allow mocking of protected methods, for that one class only: .. code-block:: php class MyClass { protected function foo() { } } $mock = \Mockery::mock('MyClass') ->shouldAllowMockingProtectedMethods(); $mock->shouldReceive('foo'); PKZ03!)).mockery/docs/reference/argument_validation.rstnuW+A.. index:: single: Argument Validation Argument Validation =================== The arguments passed to the ``with()`` declaration when setting up an expectation determine the criteria for matching method calls to expectations. Thus, we can setup up many expectations for a single method, each differentiated by the expected arguments. Such argument matching is done on a "best fit" basis. This ensures explicit matches take precedence over generalised matches. An explicit match is merely where the expected argument and the actual argument are easily equated (i.e. using ``===`` or ``==``). More generalised matches are possible using regular expressions, class hinting and the available generic matchers. The purpose of generalised matchers is to allow arguments be defined in non-explicit terms, e.g. ``Mockery::any()`` passed to ``with()`` will match **any** argument in that position. Mockery's generic matchers do not cover all possibilities but offers optional support for the Hamcrest library of matchers. Hamcrest is a PHP port of the similarly named Java library (which has been ported also to Python, Erlang, etc). By using Hamcrest, Mockery does not need to duplicate Hamcrest's already impressive utility which itself promotes a natural English DSL. The examples below show Mockery matchers and their Hamcrest equivalent, if there is one. Hamcrest uses functions (no namespacing). .. note:: If you don't wish to use the global Hamcrest functions, they are all exposed through the ``\Hamcrest\Matchers`` class as well, as static methods. Thus, ``identicalTo($arg)`` is the same as ``\Hamcrest\Matchers::identicalTo($arg)`` The most common matcher is the ``with()`` matcher: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(1): It tells mockery that it should receive a call to the ``foo`` method with the integer ``1`` as an argument. In cases like this, Mockery first tries to match the arguments using ``===`` (identical) comparison operator. If the argument is a primitive, and if it fails the identical comparison, Mockery does a fallback to the ``==`` (equals) comparison operator. When matching objects as arguments, Mockery only does the strict ``===`` comparison, which means only the same ``$object`` will match: .. code-block:: php $object = new stdClass(); $mock = \Mockery::mock('MyClass'); $mock->shouldReceive("foo") ->with($object); // Hamcrest equivalent $mock->shouldReceive("foo") ->with(identicalTo($object)); A different instance of ``stdClass`` will **not** match. .. note:: The ``Mockery\Matcher\MustBe`` matcher has been deprecated. If we need a loose comparison of objects, we can do that using Hamcrest's ``equalTo`` matcher: .. code-block:: php $mock->shouldReceive("foo") ->with(equalTo(new stdClass)); In cases when we don't care about the type, or the value of an argument, just that any argument is present, we use ``any()``: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive("foo") ->with(\Mockery::any()); // Hamcrest equivalent $mock->shouldReceive("foo") ->with(anything()) Anything and everything passed in this argument slot is passed unconstrained. Validating Types and Resources ------------------------------ The ``type()`` matcher accepts any string which can be attached to ``is_`` to form a valid type check. To match any PHP resource, we could do the following: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive("foo") ->with(\Mockery::type('resource')); // Hamcrest equivalents $mock->shouldReceive("foo") ->with(resourceValue()); $mock->shouldReceive("foo") ->with(typeOf('resource')); It will return a ``true`` from an ``is_resource()`` call, if the provided argument to the method is a PHP resource. For example, ``\Mockery::type('float')`` or Hamcrest's ``floatValue()`` and ``typeOf('float')`` checks use ``is_float()``, and ``\Mockery::type('callable')`` or Hamcrest's ``callable()`` uses ``is_callable()``. The ``type()`` matcher also accepts a class or interface name to be used in an ``instanceof`` evaluation of the actual argument. Hamcrest uses ``anInstanceOf()``. A full list of the type checkers is available at `php.net `_ or browse Hamcrest's function list in `the Hamcrest code `_. .. _argument-validation-complex-argument-validation: Complex Argument Validation --------------------------- If we want to perform a complex argument validation, the ``on()`` matcher is invaluable. It accepts a closure (anonymous function) to which the actual argument will be passed. .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive("foo") ->with(\Mockery::on(closure)); If the closure evaluates to (i.e. returns) boolean ``true`` then the argument is assumed to have matched the expectation. .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::on(function ($argument) { if ($argument % 2 == 0) { return true; } return false; })); $mock->foo(4); // matches the expectation $mock->foo(3); // throws a NoMatchingExpectationException .. note:: There is no Hamcrest version of the ``on()`` matcher. We can also perform argument validation by passing a closure to ``withArgs()`` method. The closure will receive all arguments passed in the call to the expected method and if it evaluates (i.e. returns) to boolean ``true``, then the list of arguments is assumed to have matched the expectation: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive("foo") ->withArgs(closure); The closure can also handle optional parameters, so if an optional parameter is missing in the call to the expected method, it doesn't necessary means that the list of arguments doesn't match the expectation. .. code-block:: php $closure = function ($odd, $even, $sum = null) { $result = ($odd % 2 != 0) && ($even % 2 == 0); if (!is_null($sum)) { return $result && ($odd + $even == $sum); } return $result; }; $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo')->withArgs($closure); $mock->foo(1, 2); // It matches the expectation: the optional argument is not needed $mock->foo(1, 2, 3); // It also matches the expectation: the optional argument pass the validation $mock->foo(1, 2, 4); // It doesn't match the expectation: the optional doesn't pass the validation .. note:: In previous versions, Mockery's ``with()`` would attempt to do a pattern matching against the arguments, attempting to use the argument as a regular expression. Over time this proved to be not such a great idea, so we removed this functionality, and have introduced ``Mockery::pattern()`` instead. If we would like to match an argument against a regular expression, we can use the ``\Mockery::pattern()``: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::pattern('/^foo/')); // Hamcrest equivalent $mock->shouldReceive('foo') ->with(matchesPattern('/^foo/')); The ``ducktype()`` matcher is an alternative to matching by class type: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::ducktype('foo', 'bar')); It matches any argument which is an object containing the provided list of methods to call. .. note:: There is no Hamcrest version of the ``ducktype()`` matcher. Capturing Arguments ------------------- If we want to perform multiple validations on a single argument, the ``capture`` matcher provides a streamlined alternative to using the ``on()`` matcher. It accepts a variable which the actual argument will be assigned. .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive("foo") ->with(\Mockery::capture($bar)); This will assign *any* argument passed to ``foo`` to the local ``$bar`` variable to then perform additional validation using assertions. .. note:: The ``capture`` matcher always evaluates to ``true``. As such, we should always perform additional argument validation. Additional Argument Matchers ---------------------------- The ``not()`` matcher matches any argument which is not equal or identical to the matcher's parameter: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::not(2)); // Hamcrest equivalent $mock->shouldReceive('foo') ->with(not(2)); ``anyOf()`` matches any argument which equals any one of the given parameters: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::anyOf(1, 2)); // Hamcrest equivalent $mock->shouldReceive('foo') ->with(anyOf(1,2)); ``notAnyOf()`` matches any argument which is not equal or identical to any of the given parameters: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::notAnyOf(1, 2)); .. note:: There is no Hamcrest version of the ``notAnyOf()`` matcher. ``subset()`` matches any argument which is any array containing the given array subset: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::subset(array(0 => 'foo'))); This enforces both key naming and values, i.e. both the key and value of each actual element is compared. .. note:: There is no Hamcrest version of this functionality, though Hamcrest can check a single entry using ``hasEntry()`` or ``hasKeyValuePair()``. ``contains()`` matches any argument which is an array containing the listed values: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::contains(value1, value2)); The naming of keys is ignored. ``hasKey()`` matches any argument which is an array containing the given key name: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::hasKey(key)); ``hasValue()`` matches any argument which is an array containing the given value: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::hasValue(value)); PKZϬmGG0mockery/docs/reference/final_methods_classes.rstnuW+A.. index:: single: Mocking; Final Classes/Methods Dealing with Final Classes/Methods ================================== One of the primary restrictions of mock objects in PHP, is that mocking classes or methods marked final is hard. The final keyword prevents methods so marked from being replaced in subclasses (subclassing is how mock objects can inherit the type of the class or object being mocked). The simplest solution is to implement an interface in your final class and typehint against / mock this. However this may not be possible in some third party libraries. Mockery does allow creating "proxy mocks" from classes marked final, or from classes with methods marked final. This offers all the usual mock object goodness but the resulting mock will not inherit the class type of the object being mocked, i.e. it will not pass any instanceof comparison. Methods marked as final will be proxied to the original method, i.e., final methods can't be mocked. We can create a proxy mock by passing the instantiated object we wish to mock into ``\Mockery::mock()``, i.e. Mockery will then generate a Proxy to the real object and selectively intercept method calls for the purposes of setting and meeting expectations. See the :ref:`creating-test-doubles-partial-test-doubles` chapter, the subsection about proxied partial test doubles. PKZ4%$ mockery/docs/reference/spies.rstnuW+A.. index:: single: Reference; Spies Spies ===== Spies are a type of test doubles, but they differ from stubs or mocks in that, that the spies record any interaction between the spy and the System Under Test (SUT), and allow us to make assertions against those interactions after the fact. Creating a spy means we don't have to set up expectations for every method call the double might receive during the test, some of which may not be relevant to the current test. A spy allows us to make assertions about the calls we care about for this test only, reducing the chances of over-specification and making our tests more clear. Spies also allow us to follow the more familiar Arrange-Act-Assert or Given-When-Then style within our tests. With mocks, we have to follow a less familiar style, something along the lines of Arrange-Expect-Act-Assert, where we have to tell our mocks what to expect before we act on the SUT, then assert that those expectations were met: .. code-block:: php // arrange $mock = \Mockery::mock('MyDependency'); $sut = new MyClass($mock); // expect $mock->shouldReceive('foo') ->once() ->with('bar'); // act $sut->callFoo(); // assert \Mockery::close(); Spies allow us to skip the expect part and move the assertion to after we have acted on the SUT, usually making our tests more readable: .. code-block:: php // arrange $spy = \Mockery::spy('MyDependency'); $sut = new MyClass($spy); // act $sut->callFoo(); // assert $spy->shouldHaveReceived() ->foo() ->with('bar'); On the other hand, spies are far less restrictive than mocks, meaning tests are usually less precise, as they let us get away with more. This is usually a good thing, they should only be as precise as they need to be, but while spies make our tests more intent-revealing, they do tend to reveal less about the design of the SUT. If we're having to setup lots of expectations for a mock, in lots of different tests, our tests are trying to tell us something - the SUT is doing too much and probably should be refactored. We don't get this with spies, they simply ignore the calls that aren't relevant to them. Another downside to using spies is debugging. When a mock receives a call that it wasn't expecting, it immediately throws an exception (failing fast), giving us a nice stack trace or possibly even invoking our debugger. With spies, we're simply asserting calls were made after the fact, so if the wrong calls were made, we don't have quite the same just in time context we have with the mocks. Finally, if we need to define a return value for our test double, we can't do that with a spy, only with a mock object. .. note:: This documentation page is an adaption of the blog post titled `"Mockery Spies" `_, published by Dave Marshall on his blog. Dave is the original author of spies in Mockery. Spies Reference --------------- To verify that a method was called on a spy, we use the ``shouldHaveReceived()`` method: .. code-block:: php $spy->shouldHaveReceived('foo'); To verify that a method was **not** called on a spy, we use the ``shouldNotHaveReceived()`` method: .. code-block:: php $spy->shouldNotHaveReceived('foo'); We can also do argument matching with spies: .. code-block:: php $spy->shouldHaveReceived('foo') ->with('bar'); Argument matching is also possible by passing in an array of arguments to match: .. code-block:: php $spy->shouldHaveReceived('foo', ['bar']); Although when verifying a method was not called, the argument matching can only be done by supplying the array of arguments as the 2nd argument to the ``shouldNotHaveReceived()`` method: .. code-block:: php $spy->shouldNotHaveReceived('foo', ['bar']); This is due to Mockery's internals. Finally, when expecting calls that should have been received, we can also verify the number of calls: .. code-block:: php $spy->shouldHaveReceived('foo') ->with('bar') ->twice(); Alternative shouldReceive syntax ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As of Mockery 1.0.0, we support calling methods as we would call any PHP method, and not as string arguments to Mockery ``should*`` methods. In cases of spies, this only applies to the ``shouldHaveReceived()`` method: .. code-block:: php $spy->shouldHaveReceived() ->foo('bar'); We can set expectation on number of calls as well: .. code-block:: php $spy->shouldHaveReceived() ->foo('bar') ->twice(); Unfortunately, due to limitations we can't support the same syntax for the ``shouldNotHaveReceived()`` method. PKZp(mockery/docs/reference/magic_methods.rstnuW+A.. index:: single: Mocking; Magic Methods PHP Magic Methods ================= PHP magic methods which are prefixed with a double underscore, e.g. ``__set()``, pose a particular problem in mocking and unit testing in general. It is strongly recommended that unit tests and mock objects do not directly refer to magic methods. Instead, refer only to the virtual methods and properties these magic methods simulate. Following this piece of advice will ensure we are testing the real API of classes and also ensures there is no conflict should Mockery override these magic methods, which it will inevitably do in order to support its role in intercepting method calls and properties. PKZ$mockery/docs/index.rstnuW+AMockery ======= Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending. Mock Objects ------------ In unit tests, mock objects simulate the behaviour of real objects. They are commonly utilised to offer test isolation, to stand in for objects which do not yet exist, or to allow for the exploratory design of class APIs without requiring actual implementation up front. The benefits of a mock object framework are to allow for the flexible generation of such mock objects (and stubs). They allow the setting of expected method calls and return values using a flexible API which is capable of capturing every possible real object behaviour in way that is stated as close as possible to a natural language description. Getting Started --------------- Ready to dive into the Mockery framework? Then you can get started by reading the "Getting Started" section! .. toctree:: :hidden: getting_started/index .. include:: getting_started/map.rst.inc Reference --------- The reference contains a complete overview of all features of the Mockery framework. .. toctree:: :hidden: reference/index .. include:: reference/map.rst.inc Mockery ------- Learn about Mockery's configuration, reserved method names, exceptions... .. toctree:: :hidden: mockery/index .. include:: mockery/map.rst.inc Cookbook -------- Want to learn some easy tips and tricks? Take a look at the cookbook articles! .. toctree:: :hidden: cookbook/index .. include:: cookbook/map.rst.inc PKZ>(ETTmockery/docs/README.mdnuW+Amockery-docs ============ Document for the PHP Mockery framework on readthedocs.orgPKZul%(mockery/docs/getting_started/map.rst.incnuW+A* :doc:`/getting_started/installation` * :doc:`/getting_started/upgrading` * :doc:`/getting_started/simple_example` * :doc:`/getting_started/quick_reference` PKZ]b!-mockery/docs/getting_started/installation.rstnuW+A.. index:: single: Installation Installation ============ Mockery can be installed using Composer or by cloning it from its GitHub repository. These two options are outlined below. Composer -------- You can read more about Composer on `getcomposer.org `_. To install Mockery using Composer, first install Composer for your project using the instructions on the `Composer download page `_. You can then define your development dependency on Mockery using the suggested parameters below. While every effort is made to keep the master branch stable, you may prefer to use the current stable version tag instead (use the ``@stable`` tag). .. code-block:: json { "require-dev": { "mockery/mockery": "dev-master" } } To install, you then may call: .. code-block:: bash php composer.phar update This will install Mockery as a development dependency, meaning it won't be installed when using ``php composer.phar update --no-dev`` in production. Other way to install is directly from composer command line, as below. .. code-block:: bash php composer.phar require --dev mockery/mockery Git --- The Git repository hosts the development version in its master branch. You can install this using Composer by referencing ``dev-master`` as your preferred version in your project's ``composer.json`` file as the earlier example shows. PKZH,&mockery/docs/getting_started/index.rstnuW+AGetting Started =============== .. toctree:: :hidden: installation upgrading simple_example quick_reference .. include:: map.rst.inc PKZcservice = $service; } public function average() { $total = 0; for ($i=0; $i<3; $i++) { $total += $this->service->readTemp(); } return $total/3; } } Even without an actual service class, we can see how we expect it to operate. When writing a test for the ``Temperature`` class, we can now substitute a mock object for the real service which allows us to test the behaviour of the ``Temperature`` class without actually needing a concrete service instance. .. code-block:: php use \Mockery; class TemperatureTest extends \PHPUnit\Framework\TestCase { public function tearDown() { Mockery::close(); } public function testGetsAverageTemperatureFromThreeServiceReadings() { $service = Mockery::mock('service'); $service->shouldReceive('readTemp') ->times(3) ->andReturn(10, 12, 14); $temperature = new Temperature($service); $this->assertEquals(12, $temperature->average()); } } We create a mock object which our ``Temperature`` class will use and set some expectations for that mock — that it should receive three calls to the ``readTemp`` method, and these calls will return 10, 12, and 14 as results. .. note:: PHPUnit integration can remove the need for a ``tearDown()`` method. See ":doc:`/reference/phpunit_integration`" for more information. PKZ5X0mockery/docs/getting_started/quick_reference.rstnuW+A.. index:: single: Quick Reference Quick Reference =============== The purpose of this page is to give a quick and short overview of some of the most common Mockery features. Do read the :doc:`../reference/index` to learn about all the Mockery features. Integrate Mockery with PHPUnit, either by extending the ``MockeryTestCase``: .. code-block:: php use \Mockery\Adapter\Phpunit\MockeryTestCase; class MyTest extends MockeryTestCase { } or by using the ``MockeryPHPUnitIntegration`` trait: .. code-block:: php use \PHPUnit\Framework\TestCase; use \Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; class MyTest extends TestCase { use MockeryPHPUnitIntegration; } Creating a test double: .. code-block:: php $testDouble = \Mockery::mock('MyClass'); Creating a test double that implements a certain interface: .. code-block:: php $testDouble = \Mockery::mock('MyClass, MyInterface'); Expecting a method to be called on a test double: .. code-block:: php $testDouble = \Mockery::mock('MyClass'); $testDouble->shouldReceive('foo'); Expecting a method to **not** be called on a test double: .. code-block:: php $testDouble = \Mockery::mock('MyClass'); $testDouble->shouldNotReceive('foo'); Expecting a method to be called on a test double, once, with a certain argument, and to return a value: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->once() ->with($arg) ->andReturn($returnValue); Expecting a method to be called on a test double and to return a different value for each successive call: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->andReturn(1, 2, 3); $mock->foo(); // int(1); $mock->foo(); // int(2); $mock->foo(); // int(3); $mock->foo(); // int(3); Creating a runtime partial test double: .. code-block:: php $mock = \Mockery::mock('MyClass')->makePartial(); Creating a spy: .. code-block:: php $spy = \Mockery::spy('MyClass'); Expecting that a spy should have received a method call: .. code-block:: php $spy = \Mockery::spy('MyClass'); $spy->foo(); $spy->shouldHaveReceived()->foo(); Not so simple examples ^^^^^^^^^^^^^^^^^^^^^^ Creating a mock object to return a sequence of values from a set of method calls: .. code-block:: php use \Mockery\Adapter\Phpunit\MockeryTestCase; class SimpleTest extends MockeryTestCase { public function testSimpleMock() { $mock = \Mockery::mock(array('pi' => 3.1416, 'e' => 2.71)); $this->assertEquals(3.1416, $mock->pi()); $this->assertEquals(2.71, $mock->e()); } } Creating a mock object which returns a self-chaining Undefined object for a method call: .. code-block:: php use \Mockery\Adapter\Phpunit\MockeryTestCase; class UndefinedTest extends MockeryTestCase { public function testUndefinedValues() { $mock = \Mockery::mock('mymock'); $mock->shouldReceive('divideBy')->with(0)->andReturnUndefined(); $this->assertTrue($mock->divideBy(0) instanceof \Mockery\Undefined); } } Creating a mock object with multiple query calls and a single update call: .. code-block:: php use \Mockery\Adapter\Phpunit\MockeryTestCase; class DbTest extends MockeryTestCase { public function testDbAdapter() { $mock = \Mockery::mock('db'); $mock->shouldReceive('query')->andReturn(1, 2, 3); $mock->shouldReceive('update')->with(5)->andReturn(NULL)->once(); // ... test code here using the mock } } Expecting all queries to be executed before any updates: .. code-block:: php use \Mockery\Adapter\Phpunit\MockeryTestCase; class DbTest extends MockeryTestCase { public function testQueryAndUpdateOrder() { $mock = \Mockery::mock('db'); $mock->shouldReceive('query')->andReturn(1, 2, 3)->ordered(); $mock->shouldReceive('update')->andReturn(NULL)->once()->ordered(); // ... test code here using the mock } } Creating a mock object where all queries occur after startup, but before finish, and where queries are expected with several different params: .. code-block:: php use \Mockery\Adapter\Phpunit\MockeryTestCase; class DbTest extends MockeryTestCase { public function testOrderedQueries() { $db = \Mockery::mock('db'); $db->shouldReceive('startup')->once()->ordered(); $db->shouldReceive('query')->with('CPWR')->andReturn(12.3)->once()->ordered('queries'); $db->shouldReceive('query')->with('MSFT')->andReturn(10.0)->once()->ordered('queries'); $db->shouldReceive('query')->with(\Mockery::pattern("/^....$/"))->andReturn(3.3)->atLeast()->once()->ordered('queries'); $db->shouldReceive('finish')->once()->ordered(); // ... test code here using the mock } } PKZY *mockery/docs/getting_started/upgrading.rstnuW+A.. index:: single: Upgrading Upgrading ========= Upgrading to 1.0.0 ------------------ Minimum PHP version +++++++++++++++++++ As of Mockery 1.0.0 the minimum PHP version required is 5.6. Using Mockery with PHPUnit ++++++++++++++++++++++++++ In the "old days", 0.9.x and older, the way Mockery was integrated with PHPUnit was through a PHPUnit listener. That listener would in turn call the ``\Mockery::close()`` method for us. As of 1.0.0, PHPUnit test cases where we want to use Mockery, should either use the ``\Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration`` trait, or extend the ``\Mockery\Adapter\Phpunit\MockeryTestCase`` test case. This will in turn call the ``\Mockery::close()`` method for us. Read the documentation for a detailed overview of ":doc:`/reference/phpunit_integration`". ``\Mockery\Matcher\MustBe`` is deprecated +++++++++++++++++++++++++++++++++++++++++ As of 1.0.0 the ``\Mockery\Matcher\MustBe`` matcher is deprecated and will be removed in Mockery 2.0.0. We recommend instead to use the PHPUnit equivalents of the MustBe matcher. ``allows`` and ``expects`` ++++++++++++++++++++++++++ As of 1.0.0, Mockery has two new methods to set up expectations: ``allows`` and ``expects``. This means that these methods names are now "reserved" for Mockery, or in other words classes you want to mock with Mockery, can't have methods called ``allows`` or ``expects``. Read more in the documentation about this ":doc:`/reference/alternative_should_receive_syntax`". No more implicit regex matching for string arguments ++++++++++++++++++++++++++++++++++++++++++++++++++++ When setting up string arguments in method expectations, Mockery 0.9.x and older, would try to match arguments using a regular expression in a "last attempt" scenario. As of 1.0.0, Mockery will no longer attempt to do this regex matching, but will only try first the identical operator ``===``, and failing that, the equals operator ``==``. If you want to match an argument using regular expressions, please use the new ``\Mockery\Matcher\Pattern`` matcher. Read more in the documentation about this pattern matcher in the ":doc:`/reference/argument_validation`" section. ``andThrow`` ``\Throwable`` +++++++++++++++++++++++++++ As of 1.0.0, the ``andThrow`` can now throw any ``\Throwable``. Upgrading to 0.9 ---------------- The generator was completely rewritten, so any code with a deep integration to mockery will need evaluating. Upgrading to 0.8 ---------------- Since the release of 0.8.0 the following behaviours were altered: 1. The ``shouldIgnoreMissing()`` behaviour optionally applied to mock objects returned an instance of ``\Mockery\Undefined`` when methods called did not match a known expectation. Since 0.8.0, this behaviour was switched to returning ``null`` instead. You can restore the 0.7.2 behaviour by using the following: .. code-block:: php $mock = \Mockery::mock('stdClass')->shouldIgnoreMissing()->asUndefined(); PKZwDU mockery/docs/mockery/map.rst.incnuW+A* :doc:`/mockery/configuration` * :doc:`/mockery/exceptions` * :doc:`/mockery/reserved_method_names` * :doc:`/mockery/gotchas` PKZ :emockery/docs/mockery/index.rstnuW+AMockery ======= .. toctree:: :hidden: configuration exceptions reserved_method_names gotchas .. include:: map.rst.inc PKZ_o o #mockery/docs/mockery/exceptions.rstnuW+A.. index:: single: Mockery; Exceptions Mockery Exceptions ================== Mockery throws three types of exceptions when it cannot verify a mock object. #. ``\Mockery\Exception\InvalidCountException`` #. ``\Mockery\Exception\InvalidOrderException`` #. ``\Mockery\Exception\NoMatchingExpectationException`` You can capture any of these exceptions in a try...catch block to query them for specific information which is also passed along in the exception message but is provided separately from getters should they be useful when logging or reformatting output. \Mockery\Exception\InvalidCountException ---------------------------------------- The exception class is used when a method is called too many (or too few) times and offers the following methods: * ``getMock()`` - return actual mock object * ``getMockName()`` - return the name of the mock object * ``getMethodName()`` - return the name of the method the failing expectation is attached to * ``getExpectedCount()`` - return expected calls * ``getExpectedCountComparative()`` - returns a string, e.g. ``<=`` used to compare to actual count * ``getActualCount()`` - return actual calls made with given argument constraints \Mockery\Exception\InvalidOrderException ---------------------------------------- The exception class is used when a method is called outside the expected order set using the ``ordered()`` and ``globally()`` expectation modifiers. It offers the following methods: * ``getMock()`` - return actual mock object * ``getMockName()`` - return the name of the mock object * ``getMethodName()`` - return the name of the method the failing expectation is attached to * ``getExpectedOrder()`` - returns an integer represented the expected index for which this call was expected * ``getActualOrder()`` - return the actual index at which this method call occurred. \Mockery\Exception\NoMatchingExpectationException ------------------------------------------------- The exception class is used when a method call does not match any known expectation. All expectations are uniquely identified in a mock object by the method name and the list of expected arguments. You can disable this exception and opt for returning NULL from all unexpected method calls by using the earlier mentioned shouldIgnoreMissing() behaviour modifier. This exception class offers the following methods: * ``getMock()`` - return actual mock object * ``getMockName()`` - return the name of the mock object * ``getMethodName()`` - return the name of the method the failing expectation is attached to * ``getActualArguments()`` - return actual arguments used to search for a matching expectation PKZ#xUU.mockery/docs/mockery/reserved_method_names.rstnuW+A.. index:: single: Reserved Method Names Reserved Method Names ===================== As you may have noticed, Mockery uses a number of methods called directly on all mock objects, for example ``shouldReceive()``. Such methods are necessary in order to setup expectations on the given mock, and so they cannot be implemented on the classes or objects being mocked without creating a method name collision (reported as a PHP fatal error). The methods reserved by Mockery are: * ``shouldReceive()`` * ``shouldNotReceive()`` * ``allows()`` * ``expects()`` * ``shouldAllowMockingMethod()`` * ``shouldIgnoreMissing()`` * ``asUndefined()`` * ``shouldAllowMockingProtectedMethods()`` * ``makePartial()`` * ``byDefault()`` * ``shouldHaveReceived()`` * ``shouldHaveBeenCalled()`` * ``shouldNotHaveReceived()`` * ``shouldNotHaveBeenCalled()`` In addition, all mocks utilise a set of added methods and protected properties which cannot exist on the class or object being mocked. These are far less likely to cause collisions. All properties are prefixed with ``_mockery`` and all method names with ``mockery_``. PKZ&#y&mockery/docs/mockery/configuration.rstnuW+A.. index:: single: Mockery; Configuration Mockery Global Configuration ============================ To allow for a degree of fine-tuning, Mockery utilises a singleton configuration object to store a small subset of core behaviours. The three currently present include: * Option to allow/disallow the mocking of methods which do not actually exist fulfilled (i.e. unused) * Setter/Getter for added a parameter map for internal PHP class methods (``Reflection`` cannot detect these automatically) * Option to drive if quick definitions should define a stub or a mock with an 'at least once' expectation. By default, the first behaviour is enabled. Of course, there are situations where this can lead to unintended consequences. The mocking of non-existent methods may allow mocks based on real classes/objects to fall out of sync with the actual implementations, especially when some degree of integration testing (testing of object wiring) is not being performed. You may allow or disallow this behaviour (whether for whole test suites or just select tests) by using the following call: .. code-block:: php \Mockery::getConfiguration()->allowMockingNonExistentMethods(bool); Passing a true allows the behaviour, false disallows it. It takes effect immediately until switched back. If the behaviour is detected when not allowed, it will result in an Exception being thrown at that point. Note that disallowing this behaviour should be carefully considered since it necessarily removes at least some of Mockery's flexibility. The other two methods are: .. code-block:: php \Mockery::getConfiguration()->setInternalClassMethodParamMap($class, $method, array $paramMap) \Mockery::getConfiguration()->getInternalClassMethodParamMap($class, $method) These are used to define parameters (i.e. the signature string of each) for the methods of internal PHP classes (e.g. SPL, or PECL extension classes like ext/mongo's MongoCollection. Reflection cannot analyse the parameters of internal classes. Most of the time, you never need to do this. It's mainly needed where an internal class method uses pass-by-reference for a parameter - you MUST in such cases ensure the parameter signature includes the ``&`` symbol correctly as Mockery won't correctly add it automatically for internal classes. Note that internal class parameter overriding is not available in PHP 8. This is because incompatible signatures have been reclassified as fatal errors. Finally there is the possibility to change what a quick definition produces. By default quick definitions create stubs but you can change this behaviour by asking Mockery to use 'at least once' expectations. .. code-block:: php \Mockery::getConfiguration()->getQuickDefinitions()->shouldBeCalledAtLeastOnce(bool) Passing a true allows the behaviour, false disallows it. It takes effect immediately until switched back. By doing so you can avoid the proliferating of quick definitions that accumulate overtime in your code since the test would fail in case the 'at least once' expectation is not fulfilled. Disabling reflection caching ---------------------------- Mockery heavily uses `"reflection" `_ to do it's job. To speed up things, Mockery caches internally the information it gathers via reflection. In some cases, this caching can cause problems. The **only** known situation when this occurs is when PHPUnit's ``--static-backup`` option is used. If you use ``--static-backup`` and you get an error that looks like the following: .. code-block:: php Error: Internal error: Failed to retrieve the reflection object We suggest turning off the reflection cache as so: .. code-block:: php \Mockery::getConfiguration()->disableReflectionCache(); Turning it back on can be done like so: .. code-block:: php \Mockery::getConfiguration()->enableReflectionCache(); In no other situation should you be required turn this reflection cache off. PKZY  mockery/docs/mockery/gotchas.rstnuW+A.. index:: single: Mockery; Gotchas Gotchas! ======== Mocking objects in PHP has its limitations and gotchas. Some functionality can't be mocked or can't be mocked YET! If you locate such a circumstance, please please (pretty please with sugar on top) create a new issue on GitHub so it can be documented and resolved where possible. Here is a list to note: 1. Classes containing public ``__wakeup()`` methods can be mocked but the mocked ``__wakeup()`` method will perform no actions and cannot have expectations set for it. This is necessary since Mockery must serialize and unserialize objects to avoid some ``__construct()`` insanity and attempting to mock a ``__wakeup()`` method as normal leads to a ``BadMethodCallException`` being thrown. 2. Mockery has two scenarios where real classes are replaced: Instance mocks and alias mocks. Both will generate PHP fatal errors if the real class is loaded, usually via a require or include statement. Only use these two mock types where autoloading is in place and where classes are not explicitly loaded on a per-file basis using ``require()``, ``require_once()``, etc. 3. Internal PHP classes are not entirely capable of being fully analysed using ``Reflection``. For example, ``Reflection`` cannot reveal details of expected parameters to the methods of such internal classes. As a result, there will be problems where a method parameter is defined to accept a value by reference (Mockery cannot detect this condition and will assume a pass by value on scalars and arrays). If references as internal class method parameters are needed, you should use the ``\Mockery\Configuration::setInternalClassMethodParamMap()`` method. Note, however that internal class parameter overriding is not available in PHP 8 since incompatible signatures have been reclassified as fatal errors. 4. Creating a mock implementing a certain interface with incorrect case in the interface name, and then creating a second mock implementing the same interface, but this time with the correct case, will have undefined behavior due to PHP's ``class_exists`` and related functions being case insensitive. Using the ``::class`` keyword in PHP can help you avoid these mistakes. The gotchas noted above are largely down to PHP's architecture and are assumed to be unavoidable. But - if you figure out a solution (or a better one than what may exist), let us know! PKZ<8kV)V)mockery/README.mdnuW+AMockery ======= [![Build Status](https://github.com/mockery/mockery/actions/workflows/tests.yml/badge.svg)](https://github.com/mockery/mockery/actions) [![Supported PHP Version](https://badgen.net/packagist/php/mockery/mockery?color=8892bf)](https://www.php.net/supported-versions) [![Code Coverage](https://codecov.io/gh/mockery/mockery/branch/1.6.x/graph/badge.svg?token=oxHwVM56bT)](https://codecov.io/gh/mockery/mockery) [![Type Coverage](https://shepherd.dev/github/mockery/mockery/coverage.svg)](https://shepherd.dev/github/mockery/mockery) [![Latest Stable Version](https://poser.pugx.org/mockery/mockery/v/stable.svg)](https://packagist.org/packages/mockery/mockery) [![Total Downloads](https://poser.pugx.org/mockery/mockery/downloads.svg)](https://packagist.org/packages/mockery/mockery) Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending. Mockery is released under a New BSD License. ## Installation To install Mockery, run the command below and you will get the latest version ```sh composer require --dev mockery/mockery ``` ## Documentation In older versions, this README file was the documentation for Mockery. Over time we have improved this, and have created an extensive documentation for you. Please use this README file as a starting point for Mockery, but do read the documentation to learn how to use Mockery. The current version can be seen at [docs.mockery.io](http://docs.mockery.io). ## PHPUnit Integration Mockery ships with some helpers if you are using PHPUnit. You can extend the [`Mockery\Adapter\Phpunit\MockeryTestCase`](library/Mockery/Adapter/Phpunit/MockeryTestCase.php) class instead of `PHPUnit\Framework\TestCase`, or if you are already using a custom base class for your tests, take a look at the traits available in the [`Mockery\Adapter\Phpunit`](library/Mockery/Adapter/Phpunit) namespace. ## Test Doubles Test doubles (often called mocks) simulate the behaviour of real objects. They are commonly utilised to offer test isolation, to stand in for objects which do not yet exist, or to allow for the exploratory design of class APIs without requiring actual implementation up front. The benefits of a test double framework are to allow for the flexible generation and configuration of test doubles. They allow the setting of expected method calls and/or return values using a flexible API which is capable of capturing every possible real object behaviour in way that is stated as close as possible to a natural language description. Use the `Mockery::mock` method to create a test double. ``` php $double = Mockery::mock(); ``` If you need Mockery to create a test double to satisfy a particular type hint, you can pass the type to the `mock` method. ``` php class Book {} interface BookRepository { function find($id): Book; function findAll(): array; function add(Book $book): void; } $double = Mockery::mock(BookRepository::class); ``` A detailed explanation of creating and working with test doubles is given in the documentation, [Creating test doubles](http://docs.mockery.io/en/latest/reference/creating_test_doubles.html) section. ## Method Stubs 🎫 A method stub is a mechanism for having your test double return canned responses to certain method calls. With stubs, you don't care how many times, if at all, the method is called. Stubs are used to provide indirect input to the system under test. ``` php $double->allows()->find(123)->andReturns(new Book()); $book = $double->find(123); ``` If you have used Mockery before, you might see something new in the example above — we created a method stub using `allows`, instead of the "old" `shouldReceive` syntax. This is a new feature of Mockery v1, but fear not, the trusty ol' `shouldReceive` is still here. For new users of Mockery, the above example can also be written as: ``` php $double->shouldReceive('find')->with(123)->andReturn(new Book()); $book = $double->find(123); ``` If your stub doesn't require specific arguments, you can also use this shortcut for setting up multiple calls at once: ``` php $double->allows([ "findAll" => [new Book(), new Book()], ]); ``` or ``` php $double->shouldReceive('findAll') ->andReturn([new Book(), new Book()]); ``` You can also use this shortcut, which creates a double and sets up some stubs in one call: ``` php $double = Mockery::mock(BookRepository::class, [ "findAll" => [new Book(), new Book()], ]); ``` ## Method Call Expectations 📲 A Method call expectation is a mechanism to allow you to verify that a particular method has been called. You can specify the parameters and you can also specify how many times you expect it to be called. Method call expectations are used to verify indirect output of the system under test. ``` php $book = new Book(); $double = Mockery::mock(BookRepository::class); $double->expects()->add($book); ``` During the test, Mockery accept calls to the `add` method as prescribed. After you have finished exercising the system under test, you need to tell Mockery to check that the method was called as expected, using the `Mockery::close` method. One way to do that is to add it to your `tearDown` method in PHPUnit. ``` php public function tearDown() { Mockery::close(); } ``` The `expects()` method automatically sets up an expectation that the method call (and matching parameters) is called **once and once only**. You can choose to change this if you are expecting more calls. ``` php $double->expects()->add($book)->twice(); ``` If you have used Mockery before, you might see something new in the example above — we created a method expectation using `expects`, instead of the "old" `shouldReceive` syntax. This is a new feature of Mockery v1, but same as with `allows` in the previous section, it can be written in the "old" style. For new users of Mockery, the above example can also be written as: ``` php $double->shouldReceive('find') ->with(123) ->once() ->andReturn(new Book()); $book = $double->find(123); ``` A detailed explanation of declaring expectations on method calls, please read the documentation, the [Expectation declarations](http://docs.mockery.io/en/latest/reference/expectations.html) section. After that, you can also learn about the new `allows` and `expects` methods in the [Alternative shouldReceive syntax](http://docs.mockery.io/en/latest/reference/alternative_should_receive_syntax.html) section. It is worth mentioning that one way of setting up expectations is no better or worse than the other. Under the hood, `allows` and `expects` are doing the same thing as `shouldReceive`, at times in "less words", and as such it comes to a personal preference of the programmer which way to use. ## Test Spies 🕵️ By default, all test doubles created with the `Mockery::mock` method will only accept calls that they have been configured to `allow` or `expect` (or in other words, calls that they `shouldReceive`). Sometimes we don't necessarily care about all of the calls that are going to be made to an object. To facilitate this, we can tell Mockery to ignore any calls it has not been told to expect or allow. To do so, we can tell a test double `shouldIgnoreMissing`, or we can create the double using the `Mocker::spy` shortcut. ``` php // $double = Mockery::mock()->shouldIgnoreMissing(); $double = Mockery::spy(); $double->foo(); // null $double->bar(); // null ``` Further to this, sometimes we want to have the object accept any call during the test execution and then verify the calls afterwards. For these purposes, we need our test double to act as a Spy. All mockery test doubles record the calls that are made to them for verification afterwards by default: ``` php $double->baz(123); $double->shouldHaveReceived()->baz(123); // null $double->shouldHaveReceived()->baz(12345); // Uncaught Exception Mockery\Exception\InvalidCountException... ``` Please refer to the [Spies](http://docs.mockery.io/en/latest/reference/spies.html) section of the documentation to learn more about the spies. ## Utilities 🔌 ### Global Helpers Mockery ships with a handful of global helper methods, you just need to ask Mockery to declare them. ``` php Mockery::globalHelpers(); $mock = mock(Some::class); $spy = spy(Some::class); $spy->shouldHaveReceived() ->foo(anyArgs()); ``` All of the global helpers are wrapped in a `!function_exists` call to avoid conflicts. So if you already have a global function called `spy`, Mockery will silently skip the declaring its own `spy` function. ### Testing Traits As Mockery ships with code generation capabilities, it was trivial to add functionality allowing users to create objects on the fly that use particular traits. Any abstract methods defined by the trait will be created and can have expectations or stubs configured like normal Test Doubles. ``` php trait Foo { function foo() { return $this->doFoo(); } abstract function doFoo(); } $double = Mockery::mock(Foo::class); $double->allows()->doFoo()->andReturns(123); $double->foo(); // int(123) ``` ## Versioning The Mockery team attempts to adhere to [Semantic Versioning](http://semver.org), however, some of Mockery's internals are considered private and will be open to change at any time. Just because a class isn't final, or a method isn't marked private, does not mean it constitutes part of the API we guarantee under the versioning scheme. ### Alternative Runtimes Mockery 1.3 was the last version to support HHVM 3 and PHP 5. There is no support for HHVM 4+. ## A new home for Mockery ⚠️️ Update your remotes! Mockery has transferred to a new location. While it was once at `padraic/mockery`, it is now at `mockery/mockery`. While your existing repositories will redirect transparently for any operations, take some time to transition to the new URL. ```sh $ git remote set-url upstream https://github.com/mockery/mockery.git ``` Replace `upstream` with the name of the remote you use locally; `upstream` is commonly used but you may be using something else. Run `git remote -v` to see what you're actually using. PKZd!͸mockery/COPYRIGHT.mdnuW+A# Copyright - Copyright (c) [2009](https://github.com/mockery/mockery/commit/1d96f88142abe804ab9e893a5f07933f63e9bff9), [Pádraic Brady](https://github.com/padraic) - Copyright (c) [2011](https://github.com/mockery/mockery/commit/94dbb63aab37c659f63ea6e34acc6958928b0f59), [Robert Basic](https://github.com/robertbasic) - Copyright (c) [2012](https://github.com/mockery/mockery/commit/64e3ad6960eb3202b5b91b91a4ef1cf6252f0fef), [Dave Marshall](https://github.com/davedevelopment) - Copyright (c) [2013](https://github.com/mockery/mockery/commit/270ddd0bd051251e36a5688c52fc2638a097b110), [Graham Campbell](https://github.com/GrahamCampbell) - Copyright (c) [2017](https://github.com/mockery/mockery/commit/ba28b84c416b95924886bbd64a6a2f68e863536a), [Nathanael Esayeas](https://github.com/ghostwriter) PKZ^V'=mockery/SECURITY.mdnuW+A# Security Policy ## Supported Versions | Version | Supported | | ------- | ------------------ | | `2.0.x` | `yes` | | `1.6.x` | `yes` | | `1.5.x` | `yes` | | `<1.5.x` | `no` | ## Reporting a Vulnerability To report a security vulnerability, please [`Open a draft security advisory`](https://github.com/mockery/mockery/security/advisories/new) so we can coordinate the fix and disclosure. PKZ<(mockery/composer.jsonnuW+A{ "name": "mockery/mockery", "description": "Mockery is a simple yet flexible PHP mock object framework", "license": "BSD-3-Clause", "type": "library", "keywords": [ "bdd", "library", "mock", "mock objects", "mockery", "stub", "tdd", "test", "test double", "testing" ], "authors": [ { "name": "Pádraic Brady", "email": "padraic.brady@gmail.com", "homepage": "https://github.com/padraic", "role": "Author" }, { "name": "Dave Marshall", "email": "dave.marshall@atstsolutions.co.uk", "homepage": "https://davedevelopment.co.uk", "role": "Developer" }, { "name": "Nathanael Esayeas", "email": "nathanael.esayeas@protonmail.com", "homepage": "https://github.com/ghostwriter", "role": "Lead Developer" } ], "homepage": "https://github.com/mockery/mockery", "support": { "issues": "https://github.com/mockery/mockery/issues", "source": "https://github.com/mockery/mockery", "docs": "https://docs.mockery.io/", "rss": "https://github.com/mockery/mockery/releases.atom", "security": "https://github.com/mockery/mockery/security/advisories" }, "require": { "php": ">=7.3", "lib-pcre": ">=7.0", "hamcrest/hamcrest-php": "^2.0.1" }, "require-dev": { "phpunit/phpunit": "^8.5 || ^9.6.17", "symplify/easy-coding-standard": "^12.1.14" }, "conflict": { "phpunit/phpunit": "<8.0" }, "autoload": { "psr-4": { "Mockery\\": "library/Mockery" }, "files": [ "library/helpers.php", "library/Mockery.php" ] }, "autoload-dev": { "psr-4": { "Fixture\\": "tests/Fixture/", "Mockery\\Tests\\Unit\\": "tests/Unit", "test\\": "tests/" }, "files": [ "fixtures/autoload.php", "vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest.php" ] }, "config": { "optimize-autoloader": true, "platform": { "php": "7.3.999" }, "preferred-install": "dist", "sort-packages": true }, "scripts": { "check": [ "@composer validate", "@ecs", "@test" ], "docs": "vendor/bin/phpdoc -d library -t docs/api", "ecs": [ "@ecs:fix", "@ecs:check" ], "ecs:check": "ecs check --clear-cache || true", "ecs:fix": "ecs check --clear-cache --fix", "phive": [ "tools/phive update --force-accept-unsigned", "tools/phive purge" ], "phpunit": "vendor/bin/phpunit --do-not-cache-result --colors=always", "phpunit:coverage": "@phpunit --coverage-clover=coverage.xml", "psalm": "tools/psalm --no-cache --show-info=true", "psalm:alter": "tools/psalm --no-cache --alter --allow-backwards-incompatible-changes=false --safe-types", "psalm:baseline": "@psalm --no-diff --set-baseline=psalm-baseline.xml", "psalm:dry-run": "@psalm:alter --issues=all --dry-run", "psalm:fix": "@psalm:alter --issues=UnnecessaryVarAnnotation,MissingPureAnnotation,MissingImmutableAnnotation", "psalm:security": "@psalm --no-diff --taint-analysis", "psalm:shepherd": "@psalm --no-diff --shepherd --stats --output-format=github", "test": [ "@phpunit --stop-on-defect", "@psalm", "@psalm:security", "@psalm:dry-run" ] } } PKZ(9^^mockery/.phpstorm.meta.phpnuW+A "@"])); override(\Mockery::spy(0), map(["" => "@"])); override(\Mockery::namedMock(0), map(["" => "@"])); override(\Mockery::instanceMock(0), map(["" => "@"])); override(\mock(0), map(["" => "@"])); override(\spy(0), map(["" => "@"])); override(\namedMock(0), map(["" => "@"]));PKGiZrr.readthedocs.ymlnuW+A# Read the Docs configuration file for Sphinx projects # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the OS, Python version and other tools we might need build: os: ubuntu-22.04 tools: python: "3.12" # Build documentation in the "docs/" directory with Sphinx sphinx: configuration: docs/conf.py # Build documentation in additional formats such as PDF and ePub formats: all # Build requirements for our documentation # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - requirements: docs/requirements.txt PKGiZس CONTRIBUTING.mdnuW+A# Contributing We'd love you to help out with mockery and no contribution is too small. ## Reporting Bugs Issues can be reported on the [issue tracker](https://github.com/mockery/mockery/issues). Please try and report any bugs with a minimal reproducible example, it will make things easier for other contributors and your problems will hopefully be resolved quickly. ## Requesting Features We're always interested to hear about your ideas and you can request features by creating a ticket in the [issue tracker](https://github.com/mockery/mockery/issues). We can't always guarantee someone will jump on it straight away, but putting it out there to see if anyone else is interested is a good idea. Likewise, if a feature you would like is already listed in the issue tracker, add a :+1: so that other contributors know it's a feature that would help others. ## Contributing code and documentation We loosely follow the [PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md) and [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) coding standards, but we'll probably merge any code that looks close enough. * Fork the [repository](https://github.com/mockery/mockery) on GitHub * Add the code for your feature or bug * Add some tests for your feature or bug * Optionally, but preferably, write some documentation * Optionally, update the CHANGELOG.md file with your feature or [BC](http://en.wikipedia.org/wiki/Backward_compatibility) break * Send a [Pull Request](https://help.github.com/articles/creating-a-pull-request) to the correct target branch (see below) If you have a big change or would like to discuss something, create an issue in the [issue tracker](https://github.com/mockery/mockery/issues) or jump in to \#mockery on freenode Any code you contribute must be licensed under the [BSD 3-Clause License](http://opensource.org/licenses/BSD-3-Clause). ## Target Branch Mockery may have several active branches at any one time and roughly follows a [Git Branching Model](https://igor.io/2013/10/21/git-branching-model.html). Generally, if you're developing a new feature, you want to be targeting the master branch, if it's a bug fix, you want to be targeting a release branch, e.g. 0.8. ## Testing Mockery To run the unit tests for Mockery, clone the git repository, download Composer using the instructions at [http://getcomposer.org/download/](http://getcomposer.org/download/), then install the dependencies with `php /path/to/composer.phar install`. This will install the required dev dependencies and create the autoload files required by the unit tests. You may run the `vendor/bin/phpunit` command to run the unit tests. If everything goes to plan, there will be no failed tests! ## Debugging Mockery Mockery and its code generation can be difficult to debug. A good start is to use the `RequireLoader`, which will dump the code generated by mockery to a file before requiring it, rather than using eval. This will help with stack traces, and you will be able to open the mock class in your editor. ``` php // tests/bootstrap.php Mockery::setLoader(new Mockery\Loader\RequireLoader(sys_get_temp_dir())); ``` PKGiZ}LICENSEnuW+ABSD 3-Clause License Copyright (c) 2009-2023, Pádraic Brady All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKGiZqbYUU CHANGELOG.mdnuW+A# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [1.6.12] - 2024-05-15 ### Changed - [1420: Update `psalm-baseline.xml` ](https://github.com/mockery/mockery/pull/1420) - [1419: Update e2e-test.sh](https://github.com/mockery/mockery/pull/1419) - [1413: Upgrade `phar` tools and `phive.xml` configuration](https://github.com/mockery/mockery/pull/1413) ### Fixed - [1415: Fix mocking anonymous classes](https://github.com/mockery/mockery/pull/1415) - [1411: Mocking final classes reports unresolvable type by PHPStan](https://github.com/mockery/mockery/issues/1411) - [1410: Fix PHP Doc Comments](https://github.com/mockery/mockery/pull/1410) ### Security - [1417: Bump `Jinja2` from `3.1.3` to `3.1.4` fix CVE-2024-34064](https://github.com/mockery/mockery/pull/1417) - [1412: Bump `idna` from `3.6` to `3.7` fix CVE-2024-3651](https://github.com/mockery/mockery/pull/1412) ## [1.6.11] - 2024-03-21 ### Fixed - [1407: Fix constants map generics doc comments](https://github.com/mockery/mockery/pull/1407) - [1406: Fix reserved words used to name a class, interface or trait](https://github.com/mockery/mockery/pull/1406) - [1403: Fix regression - partial construction with trait methods](https://github.com/mockery/mockery/pull/1403) - [1401: Improve `Mockery::mock()` parameter type compatibility with array typehints](https://github.com/mockery/mockery/pull/1401) ## [1.6.10] - 2024-03-19 ### Added - [1398: [PHP 8.4] Fixes for implicit nullability deprecation](https://github.com/mockery/mockery/pull/1398) ### Fixed - [1397: Fix mock method $args parameter type](https://github.com/mockery/mockery/pull/1397) - [1396: Fix `1.6.8` release](https://github.com/mockery/mockery/pull/1396) ## [1.6.9] - 2024-03-12 - [1394: Revert v1.6.8 release](https://github.com/mockery/mockery/pull/1394) ## [1.6.8] - 2024-03-12 - [1393: Changelog v1.6.8](https://github.com/mockery/mockery/pull/1393) - [1392: Refactor remaining codebase](https://github.com/mockery/mockery/pull/1392) - [1391: Update actions to use Node 20](https://github.com/mockery/mockery/pull/1391) - [1390: Update `ReadTheDocs` dependencies](https://github.com/mockery/mockery/pull/1390) - [1389: Refactor `library/Mockery/Matcher/*`](https://github.com/mockery/mockery/pull/1389) - [1388: Refactor `library/Mockery/Loader/*`](https://github.com/mockery/mockery/pull/1388) - [1387: Refactor `library/Mockery/CountValidator/*`](https://github.com/mockery/mockery/pull/1387) - [1386: Add PHPUnit 10+ attributes](https://github.com/mockery/mockery/pull/1386) - [1385: Update composer dependencies and clean up](https://github.com/mockery/mockery/pull/1385) - [1384: Update `psalm-baseline.xml`](https://github.com/mockery/mockery/pull/1384) - [1383: Refactor `library/helpers.php`](https://github.com/mockery/mockery/pull/1383) - [1382: Refactor `library/Mockery/VerificationExpectation.php`](https://github.com/mockery/mockery/pull/1382) - [1381: Refactor `library/Mockery/VerificationDirector.php`](https://github.com/mockery/mockery/pull/1381) - [1380: Refactor `library/Mockery/QuickDefinitionsConfiguration.php`](https://github.com/mockery/mockery/pull/1380) - [1379: Refactor `library/Mockery/Undefined.php`](https://github.com/mockery/mockery/pull/1379) - [1378: Refactor `library/Mockery/Reflector.php`](https://github.com/mockery/mockery/pull/1378) - [1377: Refactor `library/Mockery/ReceivedMethodCalls.php`](https://github.com/mockery/mockery/pull/1377) - [1376: Refactor `library/Mockery.php`](https://github.com/mockery/mockery/pull/1376) - [1375: Refactor `library/Mockery/MockInterface.php`](https://github.com/mockery/mockery/pull/1375) - [1374: Refactor `library/Mockery/MethodCall.php`](https://github.com/mockery/mockery/pull/1374) - [1373: Refactor `library/Mockery/LegacyMockInterface.php`](https://github.com/mockery/mockery/pull/1373) - [1372: Refactor `library/Mockery/Instantiator.php`](https://github.com/mockery/mockery/pull/1372) - [1371: Refactor `library/Mockery/HigherOrderMessage.php`](https://github.com/mockery/mockery/pull/1371) - [1370: Refactor `library/Mockery/ExpectsHigherOrderMessage.php`](https://github.com/mockery/mockery/pull/1370) - [1369: Refactor `library/Mockery/ExpectationInterface.php`](https://github.com/mockery/mockery/pull/1369) - [1368: Refactor `library/Mockery/ExpectationDirector.php`](https://github.com/mockery/mockery/pull/1368) - [1367: Refactor `library/Mockery/Expectation.php`](https://github.com/mockery/mockery/pull/1367) - [1366: Refactor `library/Mockery/Exception.php`](https://github.com/mockery/mockery/pull/1366) - [1365: Refactor `library/Mockery/Container.php`](https://github.com/mockery/mockery/pull/1365) - [1364: Refactor `library/Mockery/Configuration.php`](https://github.com/mockery/mockery/pull/1364) - [1363: Refactor `library/Mockery/CompositeExpectation.php`](https://github.com/mockery/mockery/pull/1363) - [1362: Refactor `library/Mockery/ClosureWrapper.php`](https://github.com/mockery/mockery/pull/1362) - [1361: Refactor `library/Mockery.php`](https://github.com/mockery/mockery/pull/1361) - [1360: Refactor Container](https://github.com/mockery/mockery/pull/1360) - [1355: Fix the namespace in the SubsetTest class](https://github.com/mockery/mockery/pull/1355) - [1354: Add array-like objects support to hasKey/hasValue matchers](https://github.com/mockery/mockery/pull/1354) ## [1.6.7] - 2023-12-09 ### Added - [#1338: Support PHPUnit constraints as matchers](https://github.com/mockery/mockery/pull/1338) - [#1336: Add factory methods for `IsEqual` and `IsSame` matchers](https://github.com/mockery/mockery/pull/1336) ### Fixed - [#1346: Fix test namespaces](https://github.com/mockery/mockery/pull/1346) - [#1343: Update documentation default theme and build version](https://github.com/mockery/mockery/pull/1343) - [#1329: Prevent `shouldNotReceive` from getting overridden by invocation count methods](https://github.com/mockery/mockery/pull/1329) ### Changed - [#1351: Update psalm-baseline.xml](https://github.com/mockery/mockery/pull/1351) - [#1350: Changelog v1.6.7](https://github.com/mockery/mockery/pull/1350) - [#1349: Cleanup](https://github.com/mockery/mockery/pull/1349) - [#1348: Update makefile](https://github.com/mockery/mockery/pull/1348) - [#1347: Bump phars dependencies](https://github.com/mockery/mockery/pull/1347) - [#1344: Disabled travis-ci and sensiolabs webhooks](https://github.com/mockery/mockery/issues/1344) - [#1342: Add `.readthedocs.yml` configuration](https://github.com/mockery/mockery/pull/1342) - [#1340: docs: Remove misplaced semicolumn from code snippet](https://github.com/mockery/mockery/pull/1340) ## 1.6.6 (2023-08-08) - [#1327: Changelog v1.6.6](https://github.com/mockery/mockery/pull/1327) - [#1325: Keep the file that caused an error for inspection](https://github.com/mockery/mockery/pull/1325) - [#1324: Fix Regression - Replace `+` Array Union Operator with `array_merge`](https://github.com/mockery/mockery/pull/1324) ## 1.6.5 (2023-08-05) - [#1322: Changelog v1.6.5](https://github.com/mockery/mockery/pull/1322) - [#1321: Autoload Test Fixtures Based on PHP Runtime Version](https://github.com/mockery/mockery/pull/1321) - [#1320: Clean up mocks on destruct](https://github.com/mockery/mockery/pull/1320) - [#1318: Fix misspelling in docs](https://github.com/mockery/mockery/pull/1318) - [#1316: Fix compatibility issues with PHP 7.3](https://github.com/mockery/mockery/pull/1316) - [#1315: Fix PHP 7.3 issues](https://github.com/mockery/mockery/issues/1315) - [#1314: Add Security Policy](https://github.com/mockery/mockery/pull/1314) - [#1313: Type declaration for `iterable|object`.](https://github.com/mockery/mockery/pull/1313) - [#1312: Mock disjunctive normal form types](https://github.com/mockery/mockery/pull/1312) - [#1299: Test PHP `8.3` language features](https://github.com/mockery/mockery/pull/1299) ## 1.6.4 (2023-07-19) - [#1308: Changelog v1.6.4](https://github.com/mockery/mockery/pull/1308) - [#1307: Revert `src` to `library` for `1.6.x`](https://github.com/mockery/mockery/pull/1307) ## 1.6.3 (2023-07-18) - [#1304: Remove `extra.branch-alias` and update composer information](https://github.com/mockery/mockery/pull/1304) - [#1303: Update `.gitattributes`](https://github.com/mockery/mockery/pull/1303) - [#1302: Changelog v1.6.3](https://github.com/mockery/mockery/pull/1302) - [#1301: Fix mocking classes with `new` initializers in method and attribute params on PHP 8.1](https://github.com/mockery/mockery/pull/1301) - [#1298: Update default repository branch to latest release branch](https://github.com/mockery/mockery/issues/1298) - [#1297: Update `Makefile` for contributors](https://github.com/mockery/mockery/pull/1297) - [#1294: Correct return types of Mock for phpstan](https://github.com/mockery/mockery/pull/1294) - [#1290: Rename directory `library` to `src`](https://github.com/mockery/mockery/pull/1290) - [#1288: Update codecov workflow](https://github.com/mockery/mockery/pull/1288) - [#1287: Update psalm configuration and workflow](https://github.com/mockery/mockery/pull/1287) - [#1286: Update phpunit workflow](https://github.com/mockery/mockery/pull/1286) - [#1285: Enforce the minimum required PHP version](https://github.com/mockery/mockery/pull/1285) - [#1283: Update license and copyright information](https://github.com/mockery/mockery/pull/1283) - [#1282: Create `COPYRIGHT.md` file](https://github.com/mockery/mockery/pull/1282) - [#1279: Bump `vimeo/psalm` from `5.9.0` to `5.12.0`](https://github.com/mockery/mockery/pull/1279) ## 1.6.2 (2023-06-07) - [#1276: Add `IsEqual` Argument Matcher](https://github.com/mockery/mockery/pull/1276) - [#1275: Add `IsSame` Argument Matcher](https://github.com/mockery/mockery/pull/1275) - [#1274: Update composer branch alias](https://github.com/mockery/mockery/pull/1274) - [#1271: Support PHP 8.2 `true` Literal Type](https://github.com/mockery/mockery/pull/1271) - [#1270: Support PHP 8.0 `false` Literal Type](https://github.com/mockery/mockery/pull/1270) ## 1.6.1 (2023-06-05) - [#1267 Drops support for PHP <7.4](https://github.com/mockery/mockery/pull/1267) - [#1192 Updated changelog for version 1.5.1 to include changes from #1180](https://github.com/mockery/mockery/pull/1192) - [#1196 Update example in README.md](https://github.com/mockery/mockery/pull/1196) - [#1199 Fix function parameter default enum value](https://github.com/mockery/mockery/pull/1199) - [#1205 Deal with null type in PHP8.2](https://github.com/mockery/mockery/pull/1205) - [#1208 Import MockeryTestCase fully qualified class name](https://github.com/mockery/mockery/pull/1208) - [#1210 Add support for target class attributes](https://github.com/mockery/mockery/pull/1210) - [#1212 docs: Add missing comma](https://github.com/mockery/mockery/pull/1212) - [#1216 Fixes code generation for intersection types](https://github.com/mockery/mockery/pull/1216) - [#1217 Add MockeryExceptionInterface](https://github.com/mockery/mockery/pull/1217) - [#1218 tidy: avoids require](https://github.com/mockery/mockery/pull/1218) - [#1222 Add .editorconfig](https://github.com/mockery/mockery/pull/1222) - [#1225 Switch to PSR-4 autoload](https://github.com/mockery/mockery/pull/1225) - [#1226 Refactoring risky tests](https://github.com/mockery/mockery/pull/1226) - [#1230 Add vimeo/psalm and psalm/plugin-phpunit](https://github.com/mockery/mockery/pull/1230) - [#1232 Split PHPUnit TestSuites for PHP 8.2](https://github.com/mockery/mockery/pull/1232) - [#1233 Bump actions/checkout to v3](https://github.com/mockery/mockery/pull/1233) - [#1234 Bump nick-invision/retry to v2](https://github.com/mockery/mockery/pull/1234) - [#1235 Setup Codecov for code coverage](https://github.com/mockery/mockery/pull/1235) - [#1236 Add Psalm CI Check](https://github.com/mockery/mockery/pull/1236) - [#1237 Unignore composer.lock file](https://github.com/mockery/mockery/pull/1237) - [#1239 Prevent CI run duplication](https://github.com/mockery/mockery/pull/1239) - [#1241 Add PHPUnit workflow for PHP 8.3](https://github.com/mockery/mockery/pull/1241) - [#1244 Improve ClassAttributesPass for Dynamic Properties](https://github.com/mockery/mockery/pull/1244) - [#1245 Deprecate hamcrest/hamcrest-php package](https://github.com/mockery/mockery/pull/1245) - [#1246 Add BUG_REPORT.yml Issue template](https://github.com/mockery/mockery/pull/1246) - [#1250 Deprecate PHP <=8.0](https://github.com/mockery/mockery/issues/1250) - [#1253 Prevent array to string conversion when serialising a Subset matcher](https://github.com/mockery/mockery/issues/1253) ## 1.6.0 (2023-06-05) [DELETED] This tag was deleted due to a mistake with the composer.json PHP version constraint, see [#1266](https://github.com/mockery/mockery/issues/1266) ## 1.3.6 (2022-09-07) - PHP 8.2 | Fix "Use of "parent" in callables is deprecated" notice #1169 ## 1.5.1 (2022-09-07) - [PHP 8.2] Various tests: explicitly declare properties #1170 - [PHP 8.2] Fix "Use of "parent" in callables is deprecated" notice #1169 - [PHP 8.1] Support intersection types #1164 - Handle final `__toString` methods #1162 - Only count assertions on expectations which can fail a test #1180 ## 1.5.0 (2022-01-20) - Override default call count expectations via expects() #1146 - Mock methods with static return types #1157 - Mock methods with mixed return type #1156 - Mock classes with new in initializers on PHP 8.1 #1160 - Removes redundant PHPUnitConstraint #1158 ## 1.4.4 (2021-09-13) - Fixes auto-generated return values #1144 - Adds support for tentative types #1130 - Fixes for PHP 8.1 Support (#1130 and #1140) - Add method that allows defining a set of arguments the mock should yield #1133 - Added option to configure default matchers for objects `\Mockery::getConfiguration()->setDefaultMatcher($class, $matcherClass)` #1120 ## 1.3.5 (2021-09-13) - Fix auto-generated return values with union types #1143 - Adds support for tentative types #1130 - Fixes for PHP 8.1 Support (#1130 and #1140) - Add method that allows defining a set of arguments the mock should yield #1133 - Added option to configure default matchers for objects `\Mockery::getConfiguration()->setDefaultMatcher($class, $matcherClass)` #1120 ## 1.4.3 (2021-02-24) - Fixes calls to fetchMock before initialisation #1113 - Allow shouldIgnoreMissing() to behave in a recursive fashion #1097 - Custom object formatters #766 (Needs Docs) - Fix crash on a union type including null #1106 ## 1.3.4 (2021-02-24) - Fixes calls to fetchMock before initialisation #1113 - Fix crash on a union type including null #1106 ## 1.4.2 (2020-08-11) - Fix array to string conversion in ConstantsPass (#1086) - Fixed nullable PHP 8.0 union types (#1088, #1089) - Fixed support for PHP 8.0 parent type (#1088, #1089) - Fixed PHP 8.0 mixed type support (#1088, #1089) - Fixed PHP 8.0 union return types (#1088, #1089) ## 1.4.1 (2020-07-09) - Allow quick definitions to use 'at least once' expectation `\Mockery::getConfiguration()->getQuickDefinitions()->shouldBeCalledAtLeastOnce(true)` (#1056) - Added provisional support for PHP 8.0 (#1068, #1072,#1079) - Fix mocking methods with iterable return type without specifying a return value (#1075) ## 1.3.3 (2020-08-11) - Fix array to string conversion in ConstantsPass (#1086) - Fixed nullable PHP 8.0 union types (#1088) - Fixed support for PHP 8.0 parent type (#1088) - Fixed PHP 8.0 mixed type support (#1088) - Fixed PHP 8.0 union return types (#1088) ## 1.3.2 (2020-07-09) - Fix mocking with anonymous classes (#1039) - Fix andAnyOthers() to properly match earlier expectations (#1051) - Added provisional support for PHP 8.0 (#1068, #1072,#1079) - Fix mocking methods with iterable return type without specifying a return value (#1075) ## 1.4.0 (2020-05-19) - Fix mocking with anonymous classes (#1039) - Fix andAnyOthers() to properly match earlier expectations (#1051) - Drops support for PHP < 7.3 and PHPUnit < 8 (#1059) ## 1.3.1 (2019-12-26) - Revert improved exception debugging due to BC breaks (#1032) ## 1.3.0 (2019-11-24) - Added capture `Mockery::capture` convenience matcher (#1020) - Added `andReturnArg` to echo back an argument passed to a an expectation (#992) - Improved exception debugging (#1000) - Fixed `andSet` to not reuse properties between mock objects (#1012) ## 1.2.4 (2019-09-30) - Fix a bug introduced with previous release, for empty method definition lists (#1009) ## 1.2.3 (2019-08-07) - Allow mocking classes that have allows and expects methods (#868) - Allow passing thru __call method in all mock types (experimental) (#969) - Add support for `!` to blacklist methods (#959) - Added `withSomeOfArgs` to partial match a list of args (#967) - Fix chained demeter calls with type hint (#956) ## 1.2.2 (2019-02-13) - Fix a BC breaking change for PHP 5.6/PHPUnit 5.7.27 (#947) ## 1.2.1 (2019-02-07) - Support for PHPUnit 8 (#942) - Allow mocking static methods called on instance (#938) ## 1.2.0 (2018-10-02) - Starts counting default expectations towards count (#910) - Adds workaround for some HHVM return types (#909) - Adds PhpStorm metadata support for autocomplete etc (#904) - Further attempts to support multiple PHPUnit versions (#903) - Allows setting constructor expectations on instance mocks (#900) - Adds workaround for HHVM memoization decorator (#893) - Adds experimental support for callable spys (#712) ## 1.1.0 (2018-05-08) - Allows use of string method names in allows and expects (#794) - Finalises allows and expects syntax in API (#799) - Search for handlers in a case instensitive way (#801) - Deprecate allowMockingMethodsUnnecessarily (#808) - Fix risky tests (#769) - Fix namespace in TestListener (#812) - Fixed conflicting mock names (#813) - Clean elses (#819) - Updated protected method mocking exception message (#826) - Map of constants to mock (#829) - Simplify foreach with `in_array` function (#830) - Typehinted return value on Expectation#verify. (#832) - Fix shouldNotHaveReceived with HigherOrderMessage (#842) - Deprecates shouldDeferMissing (#839) - Adds support for return type hints in Demeter chains (#848) - Adds shouldNotReceive to composite expectation (#847) - Fix internal error when using --static-backup (#845) - Adds `andAnyOtherArgs` as an optional argument matcher (#860) - Fixes namespace qualifying with namespaced named mocks (#872) - Added possibility to add Constructor-Expections on hard dependencies, read: Mockery::mock('overload:...') (#781) ## 1.0.0 (2017-09-06) - Destructors (`__destruct`) are stubbed out where it makes sense - Allow passing a closure argument to `withArgs()` to validate multiple arguments at once. - `Mockery\Adapter\Phpunit\TestListener` has been rewritten because it incorrectly marked some tests as risky. It will no longer verify mock expectations but instead check that tests do that themselves. PHPUnit 6 is required if you want to use this fail safe. - Removes SPL Class Loader - Removed object recorder feature - Bumped minimum PHP version to 5.6 - `andThrow` will now throw anything `\Throwable` - Adds `allows` and `expects` syntax - Adds optional global helpers for `mock`, `namedMock` and `spy` - Adds ability to create objects using traits - `Mockery\Matcher\MustBe` was deprecated - Marked `Mockery\MockInterface` as internal - Subset matcher matches recursively - BC BREAK - Spies return `null` by default from ignored (non-mocked) methods with nullable return type - Removed extracting getter methods of object instances - BC BREAK - Remove implicit regex matching when trying to match string arguments, introduce `\Mockery::pattern()` when regex matching is needed - Fix Mockery not getting closed in cases of failing test cases - Fix Mockery not setting properties on overloaded instance mocks - BC BREAK - Fix Mockery not trying default expectations if there is any concrete expectation - BC BREAK - Mockery's PHPUnit integration will mark a test as risky if it thinks one it's exceptions has been swallowed in PHPUnit > 5.7.6. Use `$e->dismiss()` to dismiss. ## 0.9.4 (XXXX-XX-XX) - `shouldIgnoreMissing` will respect global `allowMockingNonExistentMethods` config - Some support for variadic parameters - Hamcrest is now a required dependency - Instance mocks now respect `shouldIgnoreMissing` call on control instance - This will be the *last version to support PHP 5.3* - Added `Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration` trait - Added `makePartial` to `Mockery\MockInterface` as it was missing ## 0.9.3 (2014-12-22) - Added a basic spy implementation - Added `Mockery\Adapter\Phpunit\MockeryTestCase` for more reliable PHPUnit integration ## 0.9.2 (2014-09-03) - Some workarounds for the serialisation problems created by changes to PHP in 5.5.13, 5.4.29, 5.6. - Demeter chains attempt to reuse doubles as they see fit, so for foo->bar and foo->baz, we'll attempt to use the same foo ## 0.9.1 (2014-05-02) - Allow specifying consecutive exceptions to be thrown with `andThrowExceptions` - Allow specifying methods which can be mocked when using `Mockery\Configuration::allowMockingNonExistentMethods(false)` with `Mockery\MockInterface::shouldAllowMockingMethod($methodName)` - Added andReturnSelf method: `$mock->shouldReceive("foo")->andReturnSelf()` - `shouldIgnoreMissing` now takes an optional value that will be return instead of null, e.g. `$mock->shouldIgnoreMissing($mock)` ## 0.9.0 (2014-02-05) - Allow mocking classes with final __wakeup() method - Quick definitions are now always `byDefault` - Allow mocking of protected methods with `shouldAllowMockingProtectedMethods` - Support official Hamcrest package - Generator completely rewritten - Easily create named mocks with namedMock PKGiZOGG composer.locknuW+A{ "_readme": [ "This file locks the dependencies of your project to a known state", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], "content-hash": "e70f68192a56a148f93ad7a1c0779be3", "packages": [ { "name": "hamcrest/hamcrest-php", "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", "shasum": "" }, "require": { "php": "^5.3|^7.0|^8.0" }, "replace": { "cordoval/hamcrest-php": "*", "davedevelopment/hamcrest-php": "*", "kodova/hamcrest-php": "*" }, "require-dev": { "phpunit/php-file-iterator": "^1.4 || ^2.0", "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.1-dev" } }, "autoload": { "classmap": [ "hamcrest" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "description": "This is the PHP port of Hamcrest Matchers", "keywords": [ "test" ], "support": { "issues": "https://github.com/hamcrest/hamcrest-php/issues", "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" }, "time": "2020-07-09T08:09:16+00:00" } ], "packages-dev": [ { "name": "doctrine/instantiator", "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.16 || ^1", "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", "homepage": "https://ocramius.github.io/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", "type": "custom" }, { "url": "https://www.patreon.com/phpdoctrine", "type": "patreon" }, { "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", "type": "tidelift" } ], "time": "2022-12-30T00:15:36+00:00" }, { "name": "myclabs/deep-copy", "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { "doctrine/collections": "<1.6.8", "doctrine/common": "<2.13.3 || >=3,<3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { "files": [ "src/DeepCopy/deep_copy.php" ], "psr-4": { "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Create deep copies (clones) of your objects", "keywords": [ "clone", "copy", "duplicate", "object", "object graph" ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", "type": "tidelift" } ], "time": "2023-03-08T13:26:56+00:00" }, { "name": "nikic/php-parser", "version": "v4.18.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": ">=7.0" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" ], "type": "library", "extra": { "branch-alias": { "dev-master": "4.9-dev" } }, "autoload": { "psr-4": { "PhpParser\\": "lib/PhpParser" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Nikita Popov" } ], "description": "A PHP parser written in PHP", "keywords": [ "parser", "php" ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" }, "time": "2023-12-10T21:03:43+00:00" }, { "name": "phar-io/manifest", "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" }, { "name": "Sebastian Heuer", "email": "sebastian@phpeople.de", "role": "Developer" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "Developer" } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", "source": "https://github.com/phar-io/manifest/tree/2.0.3" }, "time": "2021-07-20T11:28:43+00:00" }, { "name": "phar-io/version", "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" }, { "name": "Sebastian Heuer", "email": "sebastian@phpeople.de", "role": "Developer" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "Developer" } ], "description": "Library for handling version information and constraints", "support": { "issues": "https://github.com/phar-io/version/issues", "source": "https://github.com/phar-io/version/tree/3.2.1" }, "time": "2022-02-21T01:04:05+00:00" }, { "name": "phpunit/php-code-coverage", "version": "9.2.30", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089", "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", "sebastian/code-unit-reverse-lookup": "^2.0.2", "sebastian/complexity": "^2.0", "sebastian/environment": "^5.1.2", "sebastian/lines-of-code": "^1.0.3", "sebastian/version": "^3.0.1", "theseer/tokenizer": "^1.2.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { "branch-alias": { "dev-master": "9.2-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ "coverage", "testing", "xunit" ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-12-22T06:47:57+00:00" }, { "name": "phpunit/php-file-iterator", "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ "filesystem", "iterator" ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2021-12-02T12:48:52+00:00" }, { "name": "phpunit/php-invoker", "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "ext-pcntl": "*", "phpunit/phpunit": "^9.3" }, "suggest": { "ext-pcntl": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.1-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Invoke callables with a timeout", "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ "process" ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T05:58:55+00:00" }, { "name": "phpunit/php-text-template", "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Simple template engine.", "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ "template" ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T05:33:50+00:00" }, { "name": "phpunit/php-timer", "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Utility class for timing", "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ "timer" ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:16:10+00:00" }, { "name": "phpunit/phpunit", "version": "9.6.17", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1a156980d78a6666721b7e8e8502fe210b587fcd", "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd", "shasum": "" }, "require": { "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", "myclabs/deep-copy": "^1.10.1", "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=7.3", "phpunit/php-code-coverage": "^9.2.28", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", "phpunit/php-timer": "^5.0.2", "sebastian/cli-parser": "^1.0.1", "sebastian/code-unit": "^1.0.6", "sebastian/comparator": "^4.0.8", "sebastian/diff": "^4.0.3", "sebastian/environment": "^5.1.3", "sebastian/exporter": "^4.0.5", "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", "sebastian/type": "^3.2", "sebastian/version": "^3.0.2" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files", "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "bin": [ "phpunit" ], "type": "library", "extra": { "branch-alias": { "dev-master": "9.6-dev" } }, "autoload": { "files": [ "src/Framework/Assert/Functions.php" ], "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "The PHP Unit Testing framework.", "homepage": "https://phpunit.de/", "keywords": [ "phpunit", "testing", "xunit" ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.17" }, "funding": [ { "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { "url": "https://github.com/sebastianbergmann", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], "time": "2024-02-23T13:14:51+00:00" }, { "name": "sebastian/cli-parser", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library for parsing CLI options", "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T06:08:49+00:00" }, { "name": "sebastian/code-unit", "version": "1.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Collection of value objects that represent the PHP code units", "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:08:54+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T05:30:19+00:00" }, { "name": "sebastian/comparator", "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/diff": "^4.0", "sebastian/exporter": "^4.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" } ], "description": "Provides the functionality to compare PHP values for equality", "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", "equality" ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", "shasum": "" }, "require": { "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library for calculating the complexity of PHP code units", "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-12-22T06:19:30+00:00" }, { "name": "sebastian/diff", "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3", "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ "diff", "udiff", "unidiff", "unified diff" ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-05-07T05:35:17+00:00" }, { "name": "sebastian/environment", "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "suggest": { "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.1-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides functionality to handle HHVM/PHP environments", "homepage": "http://www.github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", "hhvm" ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Adam Harvey", "email": "aharvey@php.net" }, { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2022-09-14T06:03:37+00:00" }, { "name": "sebastian/global-state", "version": "5.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", "reference": "bde739e7565280bda77be70044ac1047bc007e34" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", "reference": "bde739e7565280bda77be70044ac1047bc007e34", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/object-reflector": "^2.0", "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-dom": "*", "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Snapshotting of global state", "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-08-02T09:26:13+00:00" }, { "name": "sebastian/lines-of-code", "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", "shasum": "" }, "require": { "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library for counting the lines of code in PHP source code", "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-12-22T06:20:34+00:00" }, { "name": "sebastian/object-enumerator", "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { "php": ">=7.3", "sebastian/object-reflector": "^2.0", "sebastian/recursion-context": "^4.0" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:12:34+00:00" }, { "name": "sebastian/object-reflector", "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-10-26T13:14:26+00:00" }, { "name": "sebastian/recursion-context", "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides functionality to recursively process PHP variables", "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { "issues": "https://github.com/sebastianbergmann/resource-operations/issues", "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T06:45:17+00:00" }, { "name": "sebastian/type", "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.5" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.2-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2023-02-03T06:13:03+00:00" }, { "name": "sebastian/version", "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { "php": ">=7.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], "time": "2020-09-28T06:39:44+00:00" }, { "name": "symplify/easy-coding-standard", "version": "12.1.14", "source": { "type": "git", "url": "https://github.com/easy-coding-standard/easy-coding-standard.git", "reference": "e3c4a241ee36704f7cf920d5931f39693e64afd5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/e3c4a241ee36704f7cf920d5931f39693e64afd5", "reference": "e3c4a241ee36704f7cf920d5931f39693e64afd5", "shasum": "" }, "require": { "php": ">=7.2" }, "conflict": { "friendsofphp/php-cs-fixer": "<3.46", "phpcsstandards/php_codesniffer": "<3.8", "symplify/coding-standard": "<12.1" }, "bin": [ "bin/ecs" ], "type": "library", "autoload": { "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Use Coding Standard with 0-knowledge of PHP-CS-Fixer and PHP_CodeSniffer", "keywords": [ "Code style", "automation", "fixer", "static analysis" ], "support": { "issues": "https://github.com/easy-coding-standard/easy-coding-standard/issues", "source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.1.14" }, "funding": [ { "url": "https://www.paypal.me/rectorphp", "type": "custom" }, { "url": "https://github.com/tomasvotruba", "type": "github" } ], "time": "2024-02-23T13:10:40+00:00" }, { "name": "theseer/tokenizer", "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Arne Blankerts", "email": "arne@blankerts.de", "role": "Developer" } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", "source": "https://github.com/theseer/tokenizer/tree/1.2.2" }, "funding": [ { "url": "https://github.com/theseer", "type": "github" } ], "time": "2023-11-20T00:12:19+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=7.3", "lib-pcre": ">=7.0" }, "platform-dev": [], "platform-overrides": { "php": "7.3.999" }, "plugin-api-version": "2.6.0" } PKGiZUR'library/Mockery/ReceivedMethodCalls.phpnuW+AmethodCalls[] = $methodCall; } public function verify(Expectation $expectation) { foreach ($this->methodCalls as $methodCall) { if ($methodCall->getMethod() !== $expectation->getName()) { continue; } if (! $expectation->matchArgs($methodCall->getArgs())) { continue; } $expectation->verifyCall($methodCall->getArgs()); } $expectation->verify(); } } PKGiZ)-library/Mockery/ExpectsHigherOrderMessage.phpnuW+Aonce(); } } PKGiZsslibrary/Mockery/Mock.phpnuW+A_mockery_container = $container; if (!is_null($partialObject)) { $this->_mockery_partial = $partialObject; } if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()) { foreach ($this->mockery_getMethods() as $method) { if ($method->isPublic()) { $this->_mockery_mockableMethods[] = $method->getName(); } } } $this->_mockery_instanceMock = $instanceMock; $this->_mockery_parentClass = get_parent_class($this); } /** * Set expected method calls * * @param string ...$methodNames one or many methods that are expected to be called in this mock * * @return ExpectationInterface|Expectation|HigherOrderMessage */ public function shouldReceive(...$methodNames) { if ($methodNames === []) { return new HigherOrderMessage($this, 'shouldReceive'); } foreach ($methodNames as $method) { if ('' === $method) { throw new \InvalidArgumentException('Received empty method name'); } } $self = $this; $allowMockingProtectedMethods = $this->_mockery_allowMockingProtectedMethods; return \Mockery::parseShouldReturnArgs( $this, $methodNames, static function ($method) use ($self, $allowMockingProtectedMethods) { $rm = $self->mockery_getMethod($method); if ($rm) { if ($rm->isPrivate()) { throw new \InvalidArgumentException($method . '() cannot be mocked as it is a private method'); } if (!$allowMockingProtectedMethods && $rm->isProtected()) { throw new \InvalidArgumentException($method . '() cannot be mocked as it is a protected method and mocking protected methods is not enabled for the currently used mock object. Use shouldAllowMockingProtectedMethods() to enable mocking of protected methods.'); } } $director = $self->mockery_getExpectationsFor($method); if (!$director) { $director = new ExpectationDirector($method, $self); $self->mockery_setExpectationsFor($method, $director); } $expectation = new Expectation($self, $method); $director->addExpectation($expectation); return $expectation; } ); } // start method allows /** * @param mixed $something String method name or map of method => return * @return self|ExpectationInterface|Expectation|HigherOrderMessage */ public function allows($something = []) { if (is_string($something)) { return $this->shouldReceive($something); } if (empty($something)) { return $this->shouldReceive(); } foreach ($something as $method => $returnValue) { $this->shouldReceive($method)->andReturn($returnValue); } return $this; } // end method allows // start method expects /** /** * @param mixed $something String method name (optional) * @return ExpectationInterface|Expectation|ExpectsHigherOrderMessage */ public function expects($something = null) { if (is_string($something)) { return $this->shouldReceive($something)->once(); } return new ExpectsHigherOrderMessage($this); } // end method expects /** * Shortcut method for setting an expectation that a method should not be called. * * @param string ...$methodNames one or many methods that are expected not to be called in this mock * @return ExpectationInterface|Expectation|HigherOrderMessage */ public function shouldNotReceive(...$methodNames) { if ($methodNames === []) { return new HigherOrderMessage($this, 'shouldNotReceive'); } $expectation = call_user_func_array(function (string $methodNames) { return $this->shouldReceive($methodNames); }, $methodNames); $expectation->never(); return $expectation; } /** * Allows additional methods to be mocked that do not explicitly exist on mocked class * * @param string $method name of the method to be mocked * @return Mock|MockInterface|LegacyMockInterface */ public function shouldAllowMockingMethod($method) { $this->_mockery_mockableMethods[] = $method; return $this; } /** * Set mock to ignore unexpected methods and return Undefined class * @param mixed $returnValue the default return value for calls to missing functions on this mock * @param bool $recursive Specify if returned mocks should also have shouldIgnoreMissing set * @return static */ public function shouldIgnoreMissing($returnValue = null, $recursive = false) { $this->_mockery_ignoreMissing = true; $this->_mockery_ignoreMissingRecursive = $recursive; $this->_mockery_defaultReturnValue = $returnValue; return $this; } public function asUndefined() { $this->_mockery_ignoreMissing = true; $this->_mockery_defaultReturnValue = new Undefined(); return $this; } /** * @return static */ public function shouldAllowMockingProtectedMethods() { if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()) { foreach ($this->mockery_getMethods() as $method) { if ($method->isProtected()) { $this->_mockery_mockableMethods[] = $method->getName(); } } } $this->_mockery_allowMockingProtectedMethods = true; return $this; } /** * Set mock to defer unexpected methods to it's parent * * This is particularly useless for this class, as it doesn't have a parent, * but included for completeness * * @deprecated 2.0.0 Please use makePartial() instead * * @return static */ public function shouldDeferMissing() { return $this->makePartial(); } /** * Set mock to defer unexpected methods to it's parent * * It was an alias for shouldDeferMissing(), which will be removed * in 2.0.0. * * @return static */ public function makePartial() { $this->_mockery_deferMissing = true; return $this; } /** * In the event shouldReceive() accepting one or more methods/returns, * this method will switch them from normal expectations to default * expectations * * @return self */ public function byDefault() { foreach ($this->_mockery_expectations as $director) { $exps = $director->getExpectations(); foreach ($exps as $exp) { $exp->byDefault(); } } return $this; } /** * Capture calls to this mock */ public function __call($method, array $args) { return $this->_mockery_handleMethodCall($method, $args); } public static function __callStatic($method, array $args) { return self::_mockery_handleStaticMethodCall($method, $args); } /** * Forward calls to this magic method to the __call method */ #[\ReturnTypeWillChange] public function __toString() { return $this->__call('__toString', []); } /** * Iterate across all expectation directors and validate each * * @throws Exception * @return void */ public function mockery_verify() { if ($this->_mockery_verified) { return; } if (property_exists($this, '_mockery_ignoreVerification') && $this->_mockery_ignoreVerification !== null && $this->_mockery_ignoreVerification == true) { return; } $this->_mockery_verified = true; foreach ($this->_mockery_expectations as $director) { $director->verify(); } } /** * Gets a list of exceptions thrown by this mock * * @return array */ public function mockery_thrownExceptions() { return $this->_mockery_thrownExceptions; } /** * Tear down tasks for this mock * * @return void */ public function mockery_teardown() { } /** * Fetch the next available allocation order number * * @return int */ public function mockery_allocateOrder() { ++$this->_mockery_allocatedOrder; return $this->_mockery_allocatedOrder; } /** * Set ordering for a group * * @param mixed $group * @param int $order */ public function mockery_setGroup($group, $order) { $this->_mockery_groups[$group] = $order; } /** * Fetch array of ordered groups * * @return array */ public function mockery_getGroups() { return $this->_mockery_groups; } /** * Set current ordered number * * @param int $order */ public function mockery_setCurrentOrder($order) { $this->_mockery_currentOrder = $order; return $this->_mockery_currentOrder; } /** * Get current ordered number * * @return int */ public function mockery_getCurrentOrder() { return $this->_mockery_currentOrder; } /** * Validate the current mock's ordering * * @param string $method * @param int $order * @throws \Mockery\Exception * @return void */ public function mockery_validateOrder($method, $order) { if ($order < $this->_mockery_currentOrder) { $exception = new InvalidOrderException( 'Method ' . self::class . '::' . $method . '()' . ' called out of order: expected order ' . $order . ', was ' . $this->_mockery_currentOrder ); $exception->setMock($this) ->setMethodName($method) ->setExpectedOrder($order) ->setActualOrder($this->_mockery_currentOrder); throw $exception; } $this->mockery_setCurrentOrder($order); } /** * Gets the count of expectations for this mock * * @return int */ public function mockery_getExpectationCount() { $count = $this->_mockery_expectations_count; foreach ($this->_mockery_expectations as $director) { $count += $director->getExpectationCount(); } return $count; } /** * Return the expectations director for the given method * * @var string $method * @return ExpectationDirector|null */ public function mockery_setExpectationsFor($method, ExpectationDirector $director) { $this->_mockery_expectations[$method] = $director; } /** * Return the expectations director for the given method * * @var string $method * @return ExpectationDirector|null */ public function mockery_getExpectationsFor($method) { if (isset($this->_mockery_expectations[$method])) { return $this->_mockery_expectations[$method]; } } /** * Find an expectation matching the given method and arguments * * @var string $method * @var array $args * @return Expectation|null */ public function mockery_findExpectation($method, array $args) { if (!isset($this->_mockery_expectations[$method])) { return null; } $director = $this->_mockery_expectations[$method]; return $director->findExpectation($args); } /** * Return the container for this mock * * @return Container */ public function mockery_getContainer() { return $this->_mockery_container; } /** * Return the name for this mock * * @return string */ public function mockery_getName() { return self::class; } /** * @return array */ public function mockery_getMockableProperties() { return $this->_mockery_mockableProperties; } public function __isset($name) { if (false !== stripos($name, '_mockery_')) { return false; } if (!$this->_mockery_parentClass) { return false; } if (!method_exists($this->_mockery_parentClass, '__isset')) { return false; } return call_user_func($this->_mockery_parentClass . '::__isset', $name); } public function mockery_getExpectations() { return $this->_mockery_expectations; } /** * Calls a parent class method and returns the result. Used in a passthru * expectation where a real return value is required while still taking * advantage of expectation matching and call count verification. * * @param string $name * @param array $args * @return mixed */ public function mockery_callSubjectMethod($name, array $args) { if (!method_exists($this, $name) && $this->_mockery_parentClass && method_exists($this->_mockery_parentClass, '__call')) { return call_user_func($this->_mockery_parentClass . '::__call', $name, $args); } return call_user_func_array($this->_mockery_parentClass . '::' . $name, $args); } /** * @return string[] */ public function mockery_getMockableMethods() { return $this->_mockery_mockableMethods; } /** * @return bool */ public function mockery_isAnonymous() { $rfc = new \ReflectionClass($this); // PHP 8 has Stringable interface $interfaces = array_filter($rfc->getInterfaces(), static function ($i) { return $i->getName() !== 'Stringable'; }); return false === $rfc->getParentClass() && 2 === count($interfaces); } public function mockery_isInstance() { return $this->_mockery_instanceMock; } public function __wakeup() { /** * This does not add __wakeup method support. It's a blind method and any * expected __wakeup work will NOT be performed. It merely cuts off * annoying errors where a __wakeup exists but is not essential when * mocking */ } public function __destruct() { /** * Overrides real class destructor in case if class was created without original constructor */ } public function mockery_getMethod($name) { foreach ($this->mockery_getMethods() as $method) { if ($method->getName() == $name) { return $method; } } return null; } /** * @param string $name Method name. * * @return mixed Generated return value based on the declared return value of the named method. */ public function mockery_returnValueForMethod($name) { $rm = $this->mockery_getMethod($name); if ($rm === null) { return null; } $returnType = Reflector::getSimplestReturnType($rm); switch ($returnType) { case null: return null; case 'string': return ''; case 'int': return 0; case 'float': return 0.0; case 'bool': return false; case 'true': return true; case 'false': return false; case 'array': case 'iterable': return []; case 'callable': case '\Closure': return static function () : void { }; case '\Traversable': case '\Generator': $generator = static function () { yield; }; return $generator(); case 'void': return null; case 'static': return $this; case 'object': $mock = \Mockery::mock(); if ($this->_mockery_ignoreMissingRecursive) { $mock->shouldIgnoreMissing($this->_mockery_defaultReturnValue, true); } return $mock; default: $mock = \Mockery::mock($returnType); if ($this->_mockery_ignoreMissingRecursive) { $mock->shouldIgnoreMissing($this->_mockery_defaultReturnValue, true); } return $mock; } } public function shouldHaveReceived($method = null, $args = null) { if ($method === null) { return new HigherOrderMessage($this, 'shouldHaveReceived'); } $expectation = new VerificationExpectation($this, $method); if (null !== $args) { $expectation->withArgs($args); } $expectation->atLeast()->once(); $director = new VerificationDirector($this->_mockery_getReceivedMethodCalls(), $expectation); ++$this->_mockery_expectations_count; $director->verify(); return $director; } public function shouldHaveBeenCalled() { return $this->shouldHaveReceived('__invoke'); } public function shouldNotHaveReceived($method = null, $args = null) { if ($method === null) { return new HigherOrderMessage($this, 'shouldNotHaveReceived'); } $expectation = new VerificationExpectation($this, $method); if (null !== $args) { $expectation->withArgs($args); } $expectation->never(); $director = new VerificationDirector($this->_mockery_getReceivedMethodCalls(), $expectation); ++$this->_mockery_expectations_count; $director->verify(); return null; } public function shouldNotHaveBeenCalled(?array $args = null) { return $this->shouldNotHaveReceived('__invoke', $args); } protected static function _mockery_handleStaticMethodCall($method, array $args) { $associatedRealObject = \Mockery::fetchMock(self::class); try { return $associatedRealObject->__call($method, $args); } catch (BadMethodCallException $badMethodCallException) { throw new BadMethodCallException( 'Static method ' . $associatedRealObject->mockery_getName() . '::' . $method . '() does not exist on this mock object', 0, $badMethodCallException ); } } protected function _mockery_getReceivedMethodCalls() { return $this->_mockery_receivedMethodCalls ?: $this->_mockery_receivedMethodCalls = new ReceivedMethodCalls(); } /** * Called when an instance Mock was created and its constructor is getting called * * @see \Mockery\Generator\StringManipulation\Pass\InstanceMockPass * @param array $args */ protected function _mockery_constructorCalled(array $args) { if (!isset($this->_mockery_expectations['__construct']) /* _mockery_handleMethodCall runs the other checks */) { return; } $this->_mockery_handleMethodCall('__construct', $args); } protected function _mockery_findExpectedMethodHandler($method) { if (isset($this->_mockery_expectations[$method])) { return $this->_mockery_expectations[$method]; } $lowerCasedMockeryExpectations = array_change_key_case($this->_mockery_expectations, CASE_LOWER); $lowerCasedMethod = strtolower($method); return $lowerCasedMockeryExpectations[$lowerCasedMethod] ?? null; } protected function _mockery_handleMethodCall($method, array $args) { $this->_mockery_getReceivedMethodCalls()->push(new MethodCall($method, $args)); $rm = $this->mockery_getMethod($method); if ($rm && $rm->isProtected() && !$this->_mockery_allowMockingProtectedMethods) { if ($rm->isAbstract()) { return; } try { $prototype = $rm->getPrototype(); if ($prototype->isAbstract()) { return; } } catch (\ReflectionException $re) { // noop - there is no hasPrototype method } if (null === $this->_mockery_parentClass) { $this->_mockery_parentClass = get_parent_class($this); } return call_user_func_array($this->_mockery_parentClass . '::' . $method, $args); } $handler = $this->_mockery_findExpectedMethodHandler($method); if ($handler !== null && !$this->_mockery_disableExpectationMatching) { try { return $handler->call($args); } catch (NoMatchingExpectationException $e) { if (!$this->_mockery_ignoreMissing && !$this->_mockery_deferMissing) { throw $e; } } } if (!is_null($this->_mockery_partial) && (method_exists($this->_mockery_partial, $method) || method_exists($this->_mockery_partial, '__call'))) { return $this->_mockery_partial->{$method}(...$args); } if ($this->_mockery_deferMissing && is_callable($this->_mockery_parentClass . '::' . $method) && (!$this->hasMethodOverloadingInParentClass() || ($this->_mockery_parentClass && method_exists($this->_mockery_parentClass, $method)))) { return call_user_func_array($this->_mockery_parentClass . '::' . $method, $args); } if ($this->_mockery_deferMissing && $this->_mockery_parentClass && method_exists($this->_mockery_parentClass, '__call')) { return call_user_func($this->_mockery_parentClass . '::__call', $method, $args); } if ($method === '__toString') { // __toString is special because we force its addition to the class API regardless of the // original implementation. Thus, we should always return a string rather than honor // _mockery_ignoreMissing and break the API with an error. return sprintf('%s#%s', self::class, spl_object_hash($this)); } if ($this->_mockery_ignoreMissing && (\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed() || (!is_null($this->_mockery_partial) && method_exists($this->_mockery_partial, $method)) || is_callable($this->_mockery_parentClass . '::' . $method))) { if ($this->_mockery_defaultReturnValue instanceof Undefined) { return $this->_mockery_defaultReturnValue->{$method}(...$args); } if (null === $this->_mockery_defaultReturnValue) { return $this->mockery_returnValueForMethod($method); } return $this->_mockery_defaultReturnValue; } $message = 'Method ' . self::class . '::' . $method . '() does not exist on this mock object'; if (!is_null($rm)) { $message = 'Received ' . self::class . '::' . $method . '(), but no expectations were specified'; } $bmce = new BadMethodCallException($message); $this->_mockery_thrownExceptions[] = $bmce; throw $bmce; } /** * Uses reflection to get the list of all * methods within the current mock object * * @return array */ protected function mockery_getMethods() { if (static::$_mockery_methods && \Mockery::getConfiguration()->reflectionCacheEnabled()) { return static::$_mockery_methods; } if ($this->_mockery_partial !== null) { $reflected = new \ReflectionObject($this->_mockery_partial); } else { $reflected = new \ReflectionClass($this); } return static::$_mockery_methods = $reflected->getMethods(); } private function hasMethodOverloadingInParentClass() { // if there's __call any name would be callable return is_callable($this->_mockery_parentClass . '::aFunctionNameThatNoOneWouldEverUseInRealLife12345'); } /** * @return array */ private function getNonPublicMethods() { return array_map( static function ($method) { return $method->getName(); }, array_filter($this->mockery_getMethods(), static function ($method) { return !$method->isPublic(); }) ); } } PKGiZGM"library/Mockery/Matcher/IsSame.phpnuW+A'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return $this->_expected === $actual; } } PKGiZ:bm"library/Mockery/Matcher/HasKey.phpnuW+A', $this->_expected); } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { if (! is_array($actual) && ! $actual instanceof ArrayAccess) { return false; } return array_key_exists($this->_expected, (array) $actual); } } PKGiZrFHlibrary/Mockery/Matcher/Any.phpnuW+A'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return true; } } PKGiZF.gg library/Mockery/Matcher/Type.phpnuW+A_expected) . '>'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { $function = $this->_expected === 'real' ? 'is_float' : 'is_' . strtolower($this->_expected); if (function_exists($function)) { return $function($actual); } if (! is_string($this->_expected)) { return false; } if (class_exists($this->_expected) || interface_exists($this->_expected)) { return $actual instanceof $this->_expected; } return false; } } PKGiZL[>+library/Mockery/Matcher/AndAnyOtherArgs.phpnuW+A'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return true; } } PKGiZЀ0library/Mockery/Matcher/MultiArgumentClosure.phpnuW+A'; } /** * Check if the actual value matches the expected. * Actual passed by reference to preserve reference trail (where applicable) * back to the original method parameter. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return ($this->_expected)(...$actual) === true; } } PKGiZ )PPlibrary/Mockery/Matcher/Not.phpnuW+A'; } /** * Check if the actual value does not match the expected (in this * case it's specifically NOT expected). * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return $actual !== $this->_expected; } } PKGiZgFb#library/Mockery/Matcher/IsEqual.phpnuW+A'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return $this->_expected == $actual; } } PKGiZcA$library/Mockery/Matcher/NotAnyOf.phpnuW+A'; } /** * Check if the actual value does not match the expected (in this * case it's specifically NOT expected). * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { foreach ($this->_expected as $exp) { if ($actual === $exp || $actual == $exp) { return false; } } return true; } } PKGiZ "library/Mockery/Matcher/Subset.phpnuW+Aexpected = $expected; $this->strict = $strict; } /** * Return a string representation of this Matcher * * @return string */ public function __toString() { return 'formatArray($this->expected) . '>'; } /** * @param array $expected Expected subset of data * * @return Subset */ public static function loose(array $expected) { return new static($expected, false); } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { if (! is_array($actual)) { return false; } if ($this->strict) { return $actual === array_replace_recursive($actual, $this->expected); } return $actual == array_replace_recursive($actual, $this->expected); } /** * @param array $expected Expected subset of data * * @return Subset */ public static function strict(array $expected) { return new static($expected, true); } /** * Recursively format an array into the string representation for this matcher * * @return string */ protected function formatArray(array $array) { $elements = []; foreach ($array as $k => $v) { $elements[] = $k . '=' . (is_array($v) ? $this->formatArray($v) : (string) $v); } return '[' . implode(', ', $elements) . ']'; } } PKGiZCUU#library/Mockery/Matcher/Pattern.phpnuW+A'; } /** * Check if the actual value matches the expected pattern. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return preg_match($this->_expected, (string) $actual) >= 1; } } PKGiZhj>XX$library/Mockery/Matcher/Contains.phpnuW+A_expected as $v) { $elements[] = (string) $v; } return ''; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { $values = array_values($actual); foreach ($this->_expected as $exp) { $match = false; foreach ($values as $val) { if ($exp === $val || $exp == $val) { $match = true; break; } } if ($match === false) { return false; } } return true; } } PKGiZ#h$library/Mockery/Matcher/HasValue.phpnuW+A_expected . ']>'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { if (! is_array($actual) && ! $actual instanceof ArrayAccess) { return false; } return in_array($this->_expected, (array) $actual, true); } } PKGiZE<"library/Mockery/Matcher/NoArgs.phpnuW+A'; } /** * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return count($actual) === 0; } } PKGiZ_++#library/Mockery/Matcher/Closure.phpnuW+A'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return ($this->_expected)($actual) === true; } } PKGiZ*8M"library/Mockery/Matcher/MustBe.phpnuW+A'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { if (! is_object($actual)) { return $this->_expected === $actual; } return $this->_expected == $actual; } } PKGiZ;QKK,library/Mockery/Matcher/MatcherInterface.phpnuW+A_expected) . ']>'; } /** * Check if the actual value matches the expected. * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { if (! is_object($actual)) { return false; } foreach ($this->_expected as $method) { if (! method_exists($actual, $method)) { return false; } } return true; } } PKGiZ#library/Mockery/Matcher/AnyArgs.phpnuW+A'; } /** * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return true; } } PKGiZ-Ryy!library/Mockery/Matcher/AnyOf.phpnuW+A'; } /** * Check if the actual value does not match the expected (in this * case it's specifically NOT expected). * * @template TMixed * * @param TMixed $actual * * @return bool */ public function match(&$actual) { return in_array($actual, $this->_expected, true); } } PKGiZP,+library/Mockery/Matcher/MatcherAbstract.phpnuW+A_expected = $expected; } } PKGiZRPee/library/Mockery/Matcher/ArgumentListMatcher.phpnuW+Aactual; } /** * @return int */ public function getExpectedCount() { return $this->expected; } /** * @return string */ public function getExpectedCountComparative() { return $this->expectedComparative; } /** * @return string|null */ public function getMethodName() { return $this->method; } /** * @return LegacyMockInterface|null */ public function getMock() { return $this->mockObject; } /** * @throws RuntimeException * @return string|null */ public function getMockName() { $mock = $this->getMock(); if ($mock === null) { return ''; } return $mock->mockery_getName(); } /** * @param int $count * @return self */ public function setActualCount($count) { $this->actual = $count; return $this; } /** * @param int $count * @return self */ public function setExpectedCount($count) { $this->expected = $count; return $this; } /** * @param string $comp * @return self */ public function setExpectedCountComparative($comp) { if (! in_array($comp, ['=', '>', '<', '>=', '<='], true)) { throw new RuntimeException('Illegal comparative for expected call counts set: ' . $comp); } $this->expectedComparative = $comp; return $this; } /** * @param string $name * @return self */ public function setMethodName($name) { $this->method = $name; return $this; } /** * @return self */ public function setMock(LegacyMockInterface $mock) { $this->mockObject = $mock; return $this; } } PKGiZ 7library/Mockery/Exception/MockeryExceptionInterface.phpnuW+Aactual; } /** * @return int */ public function getExpectedOrder() { return $this->expected; } /** * @return string|null */ public function getMethodName() { return $this->method; } /** * @return LegacyMockInterface|null */ public function getMock() { return $this->mockObject; } /** * @return string|null */ public function getMockName() { $mock = $this->getMock(); if ($mock === null) { return $mock; } return $mock->mockery_getName(); } /** * @param int $count * * @return self */ public function setActualOrder($count) { $this->actual = $count; return $this; } /** * @param int $count * * @return self */ public function setExpectedOrder($count) { $this->expected = $count; return $this; } /** * @param string $name * * @return self */ public function setMethodName($name) { $this->method = $name; return $this; } /** * @return self */ public function setMock(LegacyMockInterface $mock) { $this->mockObject = $mock; return $this; } } PKGiZ.library/Mockery/Exception/RuntimeException.phpnuW+Adismissed = true; // we sometimes stack them $previous = $this->getPrevious(); if (! $previous instanceof self) { return; } $previous->dismiss(); } /** * @return bool */ public function dismissed() { return $this->dismissed; } } PKGiZ ߯6library/Mockery/Exception/InvalidArgumentException.phpnuW+A */ protected $actual = []; /** * @var string|null */ protected $method = null; /** * @var LegacyMockInterface|null */ protected $mockObject = null; /** * @return array */ public function getActualArguments() { return $this->actual; } /** * @return string|null */ public function getMethodName() { return $this->method; } /** * @return LegacyMockInterface|null */ public function getMock() { return $this->mockObject; } /** * @return string|null */ public function getMockName() { $mock = $this->getMock(); if ($mock === null) { return $mock; } return $mock->mockery_getName(); } /** * @todo Rename param `count` to `args` * @template TMixed * * @param array $count * @return self */ public function setActualArguments($count) { $this->actual = $count; return $this; } /** * @param string $name * @return self */ public function setMethodName($name) { $this->method = $name; return $this; } /** * @return self */ public function setMock(LegacyMockInterface $mock) { $this->mockObject = $mock; return $this; } } PKGiZ^ yrr&library/Mockery/HigherOrderMessage.phpnuW+Amock = $mock; $this->method = $method; } /** * @param string $method * @param array $args * * @return Expectation|ExpectationInterface|HigherOrderMessage */ public function __call($method, $args) { if ($this->method === 'shouldNotHaveReceived') { return $this->mock->{$this->method}($method, $args); } $expectation = $this->mock->{$this->method}($method); return $expectation->withArgs($args); } } PKGiZN))!library/Mockery/Configuration.phpnuW+A ['MY_CONST' => 123, 'OTHER_CONST' => 'foo']] * * @var array|scalar>> */ protected $_constantsMap = []; /** * Default argument matchers * * e.g. ['class' => 'matcher'] * * @var array */ protected $_defaultMatchers = []; /** * Parameter map for use with PHP internal classes. * * e.g. ['class' => ['method' => ['param1', 'param2']]] * * @var array>> */ protected $_internalClassParamMap = []; /** * Custom object formatters * * e.g. ['class' => static fn($object) => 'formatted'] * * @var array */ protected $_objectFormatters = []; /** * @var QuickDefinitionsConfiguration */ protected $_quickDefinitionsConfiguration; /** * Boolean assertion is reflection caching enabled or not. It should be * always enabled, except when using PHPUnit's --static-backup option. * * @see https://github.com/mockery/mockery/issues/268 */ protected $_reflectionCacheEnabled = true; public function __construct() { $this->_quickDefinitionsConfiguration = new QuickDefinitionsConfiguration(); } /** * Set boolean to allow/prevent unnecessary mocking of methods * * @param bool $flag * * @return void * * @deprecated since 1.4.0 */ public function allowMockingMethodsUnnecessarily($flag = true) { @trigger_error( sprintf('The %s method is deprecated and will be removed in a future version of Mockery', __METHOD__), E_USER_DEPRECATED ); $this->_allowMockingMethodsUnnecessarily = (bool) $flag; } /** * Set boolean to allow/prevent mocking of non-existent methods * * @param bool $flag * * @return void */ public function allowMockingNonExistentMethods($flag = true) { $this->_allowMockingNonExistentMethod = (bool) $flag; } /** * Disable reflection caching * * It should be always enabled, except when using * PHPUnit's --static-backup option. * * @see https://github.com/mockery/mockery/issues/268 * * @return void */ public function disableReflectionCache() { $this->_reflectionCacheEnabled = false; } /** * Enable reflection caching * * It should be always enabled, except when using * PHPUnit's --static-backup option. * * @see https://github.com/mockery/mockery/issues/268 * * @return void */ public function enableReflectionCache() { $this->_reflectionCacheEnabled = true; } /** * Get the map of constants to be used in the mock generator * * @return array|scalar>> */ public function getConstantsMap() { return $this->_constantsMap; } /** * Get the default matcher for a given class * * @param class-string $class * * @return null|class-string */ public function getDefaultMatcher($class) { $classes = []; $parentClass = $class; do { $classes[] = $parentClass; $parentClass = get_parent_class($parentClass); } while ($parentClass !== false); $classesAndInterfaces = array_merge($classes, class_implements($class)); foreach ($classesAndInterfaces as $type) { if (array_key_exists($type, $this->_defaultMatchers)) { return $this->_defaultMatchers[$type]; } } return null; } /** * Get the parameter map of an internal PHP class method * * @param class-string $class * @param string $method * * @return null|array */ public function getInternalClassMethodParamMap($class, $method) { $class = strtolower($class); $method = strtolower($method); if (! array_key_exists($class, $this->_internalClassParamMap)) { return null; } if (! array_key_exists($method, $this->_internalClassParamMap[$class])) { return null; } return $this->_internalClassParamMap[$class][$method]; } /** * Get the parameter maps of internal PHP classes * * @return array>> */ public function getInternalClassMethodParamMaps() { return $this->_internalClassParamMap; } /** * Get the object formatter for a class * * @param class-string $class * @param Closure $defaultFormatter * * @return Closure */ public function getObjectFormatter($class, $defaultFormatter) { $parentClass = $class; do { $classes[] = $parentClass; $parentClass = get_parent_class($parentClass); } while ($parentClass !== false); $classesAndInterfaces = array_merge($classes, class_implements($class)); foreach ($classesAndInterfaces as $type) { if (array_key_exists($type, $this->_objectFormatters)) { return $this->_objectFormatters[$type]; } } return $defaultFormatter; } /** * Returns the quick definitions configuration */ public function getQuickDefinitions(): QuickDefinitionsConfiguration { return $this->_quickDefinitionsConfiguration; } /** * Return flag indicating whether mocking non-existent methods allowed * * @return bool * * @deprecated since 1.4.0 */ public function mockingMethodsUnnecessarilyAllowed() { @trigger_error( sprintf('The %s method is deprecated and will be removed in a future version of Mockery', __METHOD__), E_USER_DEPRECATED ); return $this->_allowMockingMethodsUnnecessarily; } /** * Return flag indicating whether mocking non-existent methods allowed * * @return bool */ public function mockingNonExistentMethodsAllowed() { return $this->_allowMockingNonExistentMethod; } /** * Is reflection cache enabled? * * @return bool */ public function reflectionCacheEnabled() { return $this->_reflectionCacheEnabled; } /** * Remove all overridden parameter maps from internal PHP classes. * * @return void */ public function resetInternalClassMethodParamMaps() { $this->_internalClassParamMap = []; } /** * Set a map of constants to be used in the mock generator * * e.g. ['MyClass' => ['MY_CONST' => 123, 'ARRAY_CONST' => ['foo', 'bar']]] * * @param array|scalar>> $map * * @return void */ public function setConstantsMap(array $map) { $this->_constantsMap = $map; } /** * @param class-string $class * @param class-string $matcherClass * * @throws InvalidArgumentException * * @return void */ public function setDefaultMatcher($class, $matcherClass) { $isHamcrest = is_a($matcherClass, Matcher::class, true) || is_a($matcherClass, Hamcrest_Matcher::class, true); if ($isHamcrest) { @trigger_error('Hamcrest package has been deprecated and will be removed in 2.0', E_USER_DEPRECATED); } if (! $isHamcrest && ! is_a($matcherClass, MatcherInterface::class, true)) { throw new InvalidArgumentException(sprintf( "Matcher class must implement %s, '%s' given.", MatcherInterface::class, $matcherClass )); } $this->_defaultMatchers[$class] = $matcherClass; } /** * Set a parameter map (array of param signature strings) for the method of an internal PHP class. * * @param class-string $class * @param string $method * @param list $map * * @throws LogicException * * @return void */ public function setInternalClassMethodParamMap($class, $method, array $map) { if (PHP_MAJOR_VERSION > 7) { throw new LogicException( 'Internal class parameter overriding is not available in PHP 8. Incompatible signatures have been reclassified as fatal errors.' ); } $class = strtolower($class); if (! array_key_exists($class, $this->_internalClassParamMap)) { $this->_internalClassParamMap[$class] = []; } $this->_internalClassParamMap[$class][strtolower($method)] = $map; } /** * Set a custom object formatter for a class * * @param class-string $class * @param Closure $formatterCallback * * @return void */ public function setObjectFormatter($class, $formatterCallback) { $this->_objectFormatters[$class] = $formatterCallback; } } PKGiZS^:library/Mockery/CountValidator/CountValidatorInterface.phpnuW+A_limit > $n) { $exception = new InvalidCountException( 'Method ' . (string) $this->_expectation . ' from ' . $this->_expectation->getMock()->mockery_getName() . ' should be called' . PHP_EOL . ' at least ' . $this->_limit . ' times but called ' . $n . ' times.' ); $exception->setMock($this->_expectation->getMock()) ->setMethodName((string) $this->_expectation) ->setExpectedCountComparative('>=') ->setExpectedCount($this->_limit) ->setActualCount($n); throw $exception; } } } PKGiZT,library/Mockery/CountValidator/Exception.phpnuW+A_limit !== $n) { $because = $this->_expectation->getExceptionMessage(); $exception = new InvalidCountException( 'Method ' . (string) $this->_expectation . ' from ' . $this->_expectation->getMock()->mockery_getName() . ' should be called' . PHP_EOL . ' exactly ' . $this->_limit . ' times but called ' . $n . ' times.' . ($because ? ' Because ' . $this->_expectation->getExceptionMessage() : '') ); $exception->setMock($this->_expectation->getMock()) ->setMethodName((string) $this->_expectation) ->setExpectedCountComparative('=') ->setExpectedCount($this->_limit) ->setActualCount($n); throw $exception; } } } PKGiZ{4c@@)library/Mockery/CountValidator/AtMost.phpnuW+A_limit < $n) { $exception = new InvalidCountException( 'Method ' . (string) $this->_expectation . ' from ' . $this->_expectation->getMock()->mockery_getName() . ' should be called' . PHP_EOL . ' at most ' . $this->_limit . ' times but called ' . $n . ' times.' ); $exception->setMock($this->_expectation->getMock()) ->setMethodName((string) $this->_expectation) ->setExpectedCountComparative('<=') ->setExpectedCount($this->_limit) ->setActualCount($n); throw $exception; } } } PKGiZ89library/Mockery/CountValidator/CountValidatorAbstract.phpnuW+A_expectation = $expectation; $this->_limit = $limit; } /** * Checks if the validator can accept an additional nth call * * @param int $n * * @return bool */ public function isEligible($n) { return $n < $this->_limit; } /** * Validate the call count against this validator * * @param int $n * * @return bool */ abstract public function validate($n); } PKGiZQb!library/Mockery/MockInterface.phpnuW+A return * * @return Expectation|ExpectationInterface|HigherOrderMessage|self */ public function allows($something = []); /** * @param mixed $something String method name (optional) * * @return Expectation|ExpectationInterface|ExpectsHigherOrderMessage */ public function expects($something = null); } PKGiZ&,%library/Mockery/Loader/EvalLoader.phpnuW+AgetClassName(), false)) { return; } eval('?>' . $definition->getCode()); } } PKGiZ{(1!library/Mockery/Loader/Loader.phpnuW+Apath = realpath($path); } public function __destruct() { $files = array_diff(glob($this->path . DIRECTORY_SEPARATOR . 'Mockery_*.php') ?: [], [$this->lastPath]); foreach ($files as $file) { @unlink($file); } } /** * Load the given mock definition * * @return void */ public function load(MockDefinition $definition) { if (class_exists($definition->getClassName(), false)) { return; } $this->lastPath = sprintf('%s%s%s.php', $this->path, DIRECTORY_SEPARATOR, uniqid('Mockery_', false)); file_put_contents($this->lastPath, $definition->getCode()); if (file_exists($this->lastPath)) { require $this->lastPath; } } } PKGiZXr``library/Mockery/Expectation.phpnuW+A_mock = $mock; $this->_name = $name; $this->withAnyArgs(); } /** * Cloning logic */ public function __clone() { $newValidators = []; $countValidators = $this->_countValidators; foreach ($countValidators as $validator) { $newValidators[] = clone $validator; } $this->_countValidators = $newValidators; } /** * Return a string with the method name and arguments formatted * * @return string */ public function __toString() { return Mockery::formatArgs($this->_name, $this->_expectedArgs); } /** * Set a return value, or sequential queue of return values * * @param mixed ...$args * * @return self */ public function andReturn(...$args) { $this->_returnQueue = $args; return $this; } /** * Sets up a closure to return the nth argument from the expected method call * * @param int $index * * @return self */ public function andReturnArg($index) { if (! is_int($index) || $index < 0) { throw new InvalidArgumentException( 'Invalid argument index supplied. Index must be a non-negative integer.' ); } $closure = static function (...$args) use ($index) { if (array_key_exists($index, $args)) { return $args[$index]; } throw new OutOfBoundsException( 'Cannot return an argument value. No argument exists for the index ' . $index ); }; $this->_closureQueue = [$closure]; return $this; } /** * @return self */ public function andReturnFalse() { return $this->andReturn(false); } /** * Return null. This is merely a language construct for Mock describing. * * @return self */ public function andReturnNull() { return $this->andReturn(null); } /** * Set a return value, or sequential queue of return values * * @param mixed ...$args * * @return self */ public function andReturns(...$args) { return $this->andReturn(...$args); } /** * Return this mock, like a fluent interface * * @return self */ public function andReturnSelf() { return $this->andReturn($this->_mock); } /** * @return self */ public function andReturnTrue() { return $this->andReturn(true); } /** * Return a self-returning black hole object. * * @return self */ public function andReturnUndefined() { return $this->andReturn(new Undefined()); } /** * Set a closure or sequence of closures with which to generate return * values. The arguments passed to the expected method are passed to the * closures as parameters. * * @param callable ...$args * * @return self */ public function andReturnUsing(...$args) { $this->_closureQueue = $args; return $this; } /** * Set a sequential queue of return values with an array * * @return self */ public function andReturnValues(array $values) { return $this->andReturn(...$values); } /** * Register values to be set to a public property each time this expectation occurs * * @param string $name * @param array ...$values * * @return self */ public function andSet($name, ...$values) { $this->_setQueue[$name] = $values; return $this; } /** * Set Exception class and arguments to that class to be thrown * * @param string|Throwable $exception * @param string $message * @param int $code * * @return self */ public function andThrow($exception, $message = '', $code = 0, ?\Exception $previous = null) { $this->_throw = true; if (is_object($exception)) { return $this->andReturn($exception); } return $this->andReturn(new $exception($message, $code, $previous)); } /** * Set Exception classes to be thrown * * @return self */ public function andThrowExceptions(array $exceptions) { $this->_throw = true; foreach ($exceptions as $exception) { if (! is_object($exception)) { throw new Exception('You must pass an array of exception objects to andThrowExceptions'); } } return $this->andReturnValues($exceptions); } public function andThrows($exception, $message = '', $code = 0, ?\Exception $previous = null) { return $this->andThrow($exception, $message, $code, $previous); } /** * Sets up a closure that will yield each of the provided args * * @param mixed ...$args * * @return self */ public function andYield(...$args) { $closure = static function () use ($args) { foreach ($args as $arg) { yield $arg; } }; $this->_closureQueue = [$closure]; return $this; } /** * Sets next count validator to the AtLeast instance * * @return self */ public function atLeast() { $this->_countValidatorClass = AtLeast::class; return $this; } /** * Sets next count validator to the AtMost instance * * @return self */ public function atMost() { $this->_countValidatorClass = AtMost::class; return $this; } /** * Set the exception message * * @param string $message * * @return $this */ public function because($message) { $this->_because = $message; return $this; } /** * Shorthand for setting minimum and maximum constraints on call counts * * @param int $minimum * @param int $maximum */ public function between($minimum, $maximum) { return $this->atLeast()->times($minimum)->atMost()->times($maximum); } /** * Mark this expectation as being a default * * @return self */ public function byDefault() { $director = $this->_mock->mockery_getExpectationsFor($this->_name); if ($director instanceof ExpectationDirector) { $director->makeExpectationDefault($this); } return $this; } /** * @return null|string */ public function getExceptionMessage() { return $this->_because; } /** * Return the parent mock of the expectation * * @return LegacyMockInterface|MockInterface */ public function getMock() { return $this->_mock; } public function getName() { return $this->_name; } /** * Return order number * * @return int */ public function getOrderNumber() { return $this->_orderNumber; } /** * Indicates call order should apply globally * * @return self */ public function globally() { $this->_globally = true; return $this; } /** * Check if there is a constraint on call count * * @return bool */ public function isCallCountConstrained() { return $this->_countValidators !== []; } /** * Checks if this expectation is eligible for additional calls * * @return bool */ public function isEligible() { foreach ($this->_countValidators as $validator) { if (! $validator->isEligible($this->_actualCount)) { return false; } } return true; } /** * Check if passed arguments match an argument expectation * * @return bool */ public function matchArgs(array $args) { if ($this->isArgumentListMatcher()) { return $this->_matchArg($this->_expectedArgs[0], $args); } $argCount = count($args); $expectedArgsCount = count($this->_expectedArgs); if ($argCount === $expectedArgsCount) { return $this->_matchArgs($args); } $lastExpectedArgument = $this->_expectedArgs[$expectedArgsCount - 1]; if ($lastExpectedArgument instanceof AndAnyOtherArgs) { $firstCorrespondingKey = array_search($lastExpectedArgument, $this->_expectedArgs, true); $args = array_slice($args, 0, $firstCorrespondingKey); return $this->_matchArgs($args); } return false; } /** * Indicates that this expectation is never expected to be called * * @return self */ public function never() { return $this->times(0); } /** * Indicates that this expectation is expected exactly once * * @return self */ public function once() { return $this->times(1); } /** * Indicates that this expectation must be called in a specific given order * * @param string $group Name of the ordered group * * @return self */ public function ordered($group = null) { if ($this->_globally) { $this->_globalOrderNumber = $this->_defineOrdered($group, $this->_mock->mockery_getContainer()); } else { $this->_orderNumber = $this->_defineOrdered($group, $this->_mock); } $this->_globally = false; return $this; } /** * Flag this expectation as calling the original class method with * the provided arguments instead of using a return value queue. * * @return self */ public function passthru() { if ($this->_mock instanceof Mock) { throw new Exception( 'Mock Objects not created from a loaded/existing class are incapable of passing method calls through to a parent class' ); } $this->_passthru = true; return $this; } /** * Alias to andSet(). Allows the natural English construct * - set('foo', 'bar')->andReturn('bar') * * @param string $name * @param mixed $value * * @return self */ public function set($name, $value) { return $this->andSet(...func_get_args()); } /** * Indicates the number of times this expectation should occur * * @param int $limit * * @throws InvalidArgumentException * * @return self */ public function times($limit = null) { if ($limit === null) { return $this; } if (! is_int($limit)) { throw new InvalidArgumentException('The passed Times limit should be an integer value'); } if ($this->_expectedCount === 0) { @trigger_error(self::ERROR_ZERO_INVOCATION, E_USER_DEPRECATED); // throw new \InvalidArgumentException(self::ERROR_ZERO_INVOCATION); } if ($limit === 0) { $this->_countValidators = []; } $this->_expectedCount = $limit; $this->_countValidators[$this->_countValidatorClass] = new $this->_countValidatorClass($this, $limit); if ($this->_countValidatorClass !== Exact::class) { $this->_countValidatorClass = Exact::class; unset($this->_countValidators[$this->_countValidatorClass]); } return $this; } /** * Indicates that this expectation is expected exactly twice * * @return self */ public function twice() { return $this->times(2); } /** * Verify call order * * @return void */ public function validateOrder() { if ($this->_orderNumber) { $this->_mock->mockery_validateOrder((string) $this, $this->_orderNumber, $this->_mock); } if ($this->_globalOrderNumber) { $this->_mock->mockery_getContainer()->mockery_validateOrder( (string) $this, $this->_globalOrderNumber, $this->_mock ); } } /** * Verify this expectation * * @return void */ public function verify() { foreach ($this->_countValidators as $validator) { $validator->validate($this->_actualCount); } } /** * Verify the current call, i.e. that the given arguments match those * of this expectation * * @throws Throwable * * @return mixed */ public function verifyCall(array $args) { $this->validateOrder(); ++$this->_actualCount; if ($this->_passthru === true) { return $this->_mock->mockery_callSubjectMethod($this->_name, $args); } $return = $this->_getReturnValue($args); $this->throwAsNecessary($return); $this->_setValues(); return $return; } /** * Expected argument setter for the expectation * * @param mixed ...$args * * @return self */ public function with(...$args) { return $this->withArgs($args); } /** * Set expectation that any arguments are acceptable * * @return self */ public function withAnyArgs() { $this->_expectedArgs = [new AnyArgs()]; return $this; } /** * Expected arguments for the expectation passed as an array or a closure that matches each passed argument on * each function call. * * @param array|Closure $argsOrClosure * * @return self */ public function withArgs($argsOrClosure) { if (is_array($argsOrClosure)) { return $this->withArgsInArray($argsOrClosure); } if ($argsOrClosure instanceof Closure) { return $this->withArgsMatchedByClosure($argsOrClosure); } throw new InvalidArgumentException(sprintf( 'Call to %s with an invalid argument (%s), only array and closure are allowed', __METHOD__, $argsOrClosure )); } /** * Set with() as no arguments expected * * @return self */ public function withNoArgs() { $this->_expectedArgs = [new NoArgs()]; return $this; } /** * Expected arguments should partially match the real arguments * * @param mixed ...$expectedArgs * * @return self */ public function withSomeOfArgs(...$expectedArgs) { return $this->withArgs(static function (...$args) use ($expectedArgs): bool { foreach ($expectedArgs as $expectedArg) { if (! in_array($expectedArg, $args, true)) { return false; } } return true; }); } /** * Indicates this expectation should occur zero or more times * * @return self */ public function zeroOrMoreTimes() { return $this->atLeast()->never(); } /** * Setup the ordering tracking on the mock or mock container * * @param string $group * @param object $ordering * * @return int */ protected function _defineOrdered($group, $ordering) { $groups = $ordering->mockery_getGroups(); if ($group === null) { return $ordering->mockery_allocateOrder(); } if (array_key_exists($group, $groups)) { return $groups[$group]; } $result = $ordering->mockery_allocateOrder(); $ordering->mockery_setGroup($group, $result); return $result; } /** * Fetch the return value for the matching args * * @return mixed */ protected function _getReturnValue(array $args) { $closureQueueCount = count($this->_closureQueue); if ($closureQueueCount > 1) { return array_shift($this->_closureQueue)(...$args); } if ($closureQueueCount > 0) { return current($this->_closureQueue)(...$args); } $returnQueueCount = count($this->_returnQueue); if ($returnQueueCount > 1) { return array_shift($this->_returnQueue); } if ($returnQueueCount > 0) { return current($this->_returnQueue); } return $this->_mock->mockery_returnValueForMethod($this->_name); } /** * Check if passed argument matches an argument expectation * * @param mixed $expected * @param mixed $actual * * @return bool */ protected function _matchArg($expected, &$actual) { if ($expected === $actual) { return true; } if ($expected instanceof MatcherInterface) { return $expected->match($actual); } if ($expected instanceof Constraint) { return (bool) $expected->evaluate($actual, '', true); } if ($expected instanceof Matcher || $expected instanceof Hamcrest_Matcher) { @trigger_error('Hamcrest package has been deprecated and will be removed in 2.0', E_USER_DEPRECATED); return $expected->matches($actual); } if (is_object($expected)) { $matcher = Mockery::getConfiguration()->getDefaultMatcher(get_class($expected)); return $matcher === null ? false : $this->_matchArg(new $matcher($expected), $actual); } if (is_object($actual) && is_string($expected) && $actual instanceof $expected) { return true; } return $expected == $actual; } /** * Check if the passed arguments match the expectations, one by one. * * @param array $args * * @return bool */ protected function _matchArgs($args) { for ($index = 0, $argCount = count($args); $index < $argCount; ++$index) { $param = &$args[$index]; if (! $this->_matchArg($this->_expectedArgs[$index], $param)) { return false; } } return true; } /** * Sets public properties with queued values to the mock object * * @return void */ protected function _setValues() { $mockClass = get_class($this->_mock); $container = $this->_mock->mockery_getContainer(); $mocks = $container->getMocks(); foreach ($this->_setQueue as $name => &$values) { if ($values === []) { continue; } $value = array_shift($values); $this->_mock->{$name} = $value; foreach ($mocks as $mock) { if (! $mock instanceof $mockClass) { continue; } if (! $mock->mockery_isInstance()) { continue; } $mock->{$name} = $value; } } } /** * @template TExpectedArg * * @param TExpectedArg $expectedArg * * @return bool */ private function isAndAnyOtherArgumentsMatcher($expectedArg) { return $expectedArg instanceof AndAnyOtherArgs; } /** * Check if the registered expectation is an ArgumentListMatcher * * @return bool */ private function isArgumentListMatcher() { return $this->_expectedArgs !== [] && $this->_expectedArgs[0] instanceof ArgumentListMatcher; } /** * Throws an exception if the expectation has been configured to do so * * @param Throwable $return * * @throws Throwable * * @return void */ private function throwAsNecessary($return) { if (! $this->_throw) { return; } if (! $return instanceof Throwable) { return; } throw $return; } /** * Expected arguments for the expectation passed as an array * * @return self */ private function withArgsInArray(array $arguments) { if ($arguments === []) { return $this->withNoArgs(); } $this->_expectedArgs = $arguments; return $this; } /** * Expected arguments have to be matched by the given closure. * * @return self */ private function withArgsMatchedByClosure(Closure $closure) { $this->_expectedArgs = [new MultiArgumentClosure($closure)]; return $this; } } PKGiZ.j<<(library/Mockery/VerificationDirector.phpnuW+AreceivedMethodCalls = $receivedMethodCalls; $this->expectation = $expectation; } /** * @return self */ public function atLeast() { return $this->cloneWithoutCountValidatorsApplyAndVerify('atLeast', []); } /** * @return self */ public function atMost() { return $this->cloneWithoutCountValidatorsApplyAndVerify('atMost', []); } /** * @param int $minimum * @param int $maximum * * @return self */ public function between($minimum, $maximum) { return $this->cloneWithoutCountValidatorsApplyAndVerify('between', [$minimum, $maximum]); } /** * @return self */ public function once() { return $this->cloneWithoutCountValidatorsApplyAndVerify('once', []); } /** * @param int $limit * * @return self */ public function times($limit = null) { return $this->cloneWithoutCountValidatorsApplyAndVerify('times', [$limit]); } /** * @return self */ public function twice() { return $this->cloneWithoutCountValidatorsApplyAndVerify('twice', []); } public function verify() { $this->receivedMethodCalls->verify($this->expectation); } /** * @template TArgs * * @param TArgs $args * * @return self */ public function with(...$args) { return $this->cloneApplyAndVerify('with', $args); } /** * @return self */ public function withAnyArgs() { return $this->cloneApplyAndVerify('withAnyArgs', []); } /** * @template TArgs * * @param TArgs $args * * @return self */ public function withArgs($args) { return $this->cloneApplyAndVerify('withArgs', [$args]); } /** * @return self */ public function withNoArgs() { return $this->cloneApplyAndVerify('withNoArgs', []); } /** * @param string $method * @param array $args * * @return self */ protected function cloneApplyAndVerify($method, $args) { $verificationExpectation = clone $this->expectation; $verificationExpectation->{$method}(...$args); $verificationDirector = new self($this->receivedMethodCalls, $verificationExpectation); $verificationDirector->verify(); return $verificationDirector; } /** * @param string $method * @param array $args * * @return self */ protected function cloneWithoutCountValidatorsApplyAndVerify($method, $args) { $verificationExpectation = clone $this->expectation; $verificationExpectation->clearCountValidators(); $verificationExpectation->{$method}(...$args); $verificationDirector = new self($this->receivedMethodCalls, $verificationExpectation); $verificationDirector->verify(); return $verificationDirector; } } PKGiZ:kiHiHlibrary/Mockery/Container.phpnuW+A */ protected $_groups = []; /** * @var LoaderInterface */ protected $_loader; /** * Store of mock objects * * @var array|array-key,LegacyMockInterface&MockInterface&TMockObject> */ protected $_mocks = []; /** * @var array */ protected $_namedMocks = []; /** * @var Instantiator */ protected $instantiator; public function __construct(?Generator $generator = null, ?LoaderInterface $loader = null, ?Instantiator $instantiator = null) { $this->_generator = $generator instanceof Generator ? $generator : Mockery::getDefaultGenerator(); $this->_loader = $loader instanceof LoaderInterface ? $loader : Mockery::getDefaultLoader(); $this->instantiator = $instantiator instanceof Instantiator ? $instantiator : new Instantiator(); } /** * Return a specific remembered mock according to the array index it * was stored to in this container instance * * @template TMock of object * * @param class-string $reference * * @return null|(LegacyMockInterface&MockInterface&TMock) */ public function fetchMock($reference) { return $this->_mocks[$reference] ?? null; } /** * @return Generator */ public function getGenerator() { return $this->_generator; } /** * @param string $method * @param string $parent * * @return null|string */ public function getKeyOfDemeterMockFor($method, $parent) { $keys = array_keys($this->_mocks); $match = preg_grep('/__demeter_' . md5($parent) . sprintf('_%s$/', $method), $keys); if ($match === false) { return null; } if ($match === []) { return null; } return array_values($match)[0]; } /** * @return LoaderInterface */ public function getLoader() { return $this->_loader; } /** * @template TMock of object * @return array|array-key,LegacyMockInterface&MockInterface&TMockObject> */ public function getMocks() { return $this->_mocks; } /** * @return void */ public function instanceMock() { } /** * see http://php.net/manual/en/language.oop5.basic.php * * @param string $className * * @return bool */ public function isValidClassName($className) { if ($className[0] === '\\') { $className = substr($className, 1); // remove the first backslash } // all the namespaces and class name should match the regex return array_filter( explode('\\', $className), static function ($name): bool { return ! preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $name); } ) === []; } /** * Generates a new mock object for this container * * I apologies in advance for this. A God Method just fits the API which * doesn't require differentiating between classes, interfaces, abstracts, * names or partials - just so long as it's something that can be mocked. * I'll refactor it one day so it's easier to follow. * * @template TMock of object * * @param array|TMock|Closure(LegacyMockInterface&MockInterface&TMock):LegacyMockInterface&MockInterface&TMock|array> $args * * @throws ReflectionException|RuntimeException * * @return LegacyMockInterface&MockInterface&TMock */ public function mock(...$args) { /** @var null|MockConfigurationBuilder $builder */ $builder = null; /** @var null|callable $expectationClosure */ $expectationClosure = null; $partialMethods = null; $quickDefinitions = []; $constructorArgs = null; $blocks = []; if (count($args) > 1) { $finalArg = array_pop($args); if (is_callable($finalArg) && is_object($finalArg)) { $expectationClosure = $finalArg; } else { $args[] = $finalArg; } } foreach ($args as $k => $arg) { if ($arg instanceof MockConfigurationBuilder) { $builder = $arg; unset($args[$k]); } } reset($args); $builder = $builder ?? new MockConfigurationBuilder(); $mockeryConfiguration = Mockery::getConfiguration(); $builder->setParameterOverrides($mockeryConfiguration->getInternalClassMethodParamMaps()); $builder->setConstantsMap($mockeryConfiguration->getConstantsMap()); while ($args !== []) { $arg = array_shift($args); // check for multiple interfaces if (is_string($arg)) { foreach (explode('|', $arg) as $type) { if ($arg === 'null') { // skip PHP 8 'null's continue; } if (strpos($type, ',') && !strpos($type, ']')) { $interfaces = explode(',', str_replace(' ', '', $type)); $builder->addTargets($interfaces); continue; } if (strpos($type, 'alias:') === 0) { $type = str_replace('alias:', '', $type); $builder->addTarget('stdClass'); $builder->setName($type); continue; } if (strpos($type, 'overload:') === 0) { $type = str_replace('overload:', '', $type); $builder->setInstanceMock(true); $builder->addTarget('stdClass'); $builder->setName($type); continue; } if ($type[strlen($type) - 1] === ']') { $parts = explode('[', $type); $class = $parts[0]; if (! class_exists($class, true) && ! interface_exists($class, true)) { throw new Exception('Can only create a partial mock from an existing class or interface'); } $builder->addTarget($class); $partialMethods = array_filter( explode(',', strtolower(rtrim(str_replace(' ', '', $parts[1]), ']'))) ); foreach ($partialMethods as $partialMethod) { if ($partialMethod[0] === '!') { $builder->addBlackListedMethod(substr($partialMethod, 1)); continue; } $builder->addWhiteListedMethod($partialMethod); } continue; } if (class_exists($type, true) || interface_exists($type, true) || trait_exists($type, true)) { $builder->addTarget($type); continue; } if (! $mockeryConfiguration->mockingNonExistentMethodsAllowed()) { throw new Exception(sprintf("Mockery can't find '%s' so can't mock it", $type)); } if (! $this->isValidClassName($type)) { throw new Exception('Class name contains invalid characters'); } $builder->addTarget($type); // unions are "sum" types and not "intersections", and so we must only process the first part break; } continue; } if (is_object($arg)) { $builder->addTarget($arg); continue; } if (is_array($arg)) { if ([] !== $arg && array_keys($arg) !== range(0, count($arg) - 1)) { // if associative array if (array_key_exists(self::BLOCKS, $arg)) { $blocks = $arg[self::BLOCKS]; } unset($arg[self::BLOCKS]); $quickDefinitions = $arg; continue; } $constructorArgs = $arg; continue; } throw new Exception(sprintf( 'Unable to parse arguments sent to %s::mock()', get_class($this) )); } $builder->addBlackListedMethods($blocks); if ($constructorArgs !== null) { $builder->addBlackListedMethod('__construct'); // we need to pass through } else { $builder->setMockOriginalDestructor(true); } if ($partialMethods !== null && $constructorArgs === null) { $constructorArgs = []; } $config = $builder->getMockConfiguration(); $this->checkForNamedMockClashes($config); $def = $this->getGenerator()->generate($config); $className = $def->getClassName(); if (class_exists($className, $attemptAutoload = false)) { $rfc = new ReflectionClass($className); if (! $rfc->implementsInterface(LegacyMockInterface::class)) { throw new RuntimeException(sprintf('Could not load mock %s, class already exists', $className)); } } $this->getLoader()->load($def); $mock = $this->_getInstance($className, $constructorArgs); $mock->mockery_init($this, $config->getTargetObject(), $config->isInstanceMock()); if ($quickDefinitions !== []) { if ($mockeryConfiguration->getQuickDefinitions()->shouldBeCalledAtLeastOnce()) { $mock->shouldReceive($quickDefinitions)->atLeast()->once(); } else { $mock->shouldReceive($quickDefinitions)->byDefault(); } } // if the last parameter passed to mock() is a closure, if ($expectationClosure instanceof Closure) { // call the closure with the mock object $expectationClosure($mock); } return $this->rememberMock($mock); } /** * Fetch the next available allocation order number * * @return int */ public function mockery_allocateOrder() { return ++$this->_allocatedOrder; } /** * Reset the container to its original state * * @return void */ public function mockery_close() { foreach ($this->_mocks as $mock) { $mock->mockery_teardown(); } $this->_mocks = []; } /** * Get current ordered number * * @return int */ public function mockery_getCurrentOrder() { return $this->_currentOrder; } /** * Gets the count of expectations on the mocks * * @return int */ public function mockery_getExpectationCount() { $count = 0; foreach ($this->_mocks as $mock) { $count += $mock->mockery_getExpectationCount(); } return $count; } /** * Fetch array of ordered groups * * @return array */ public function mockery_getGroups() { return $this->_groups; } /** * Set current ordered number * * @param int $order * * @return int The current order number that was set */ public function mockery_setCurrentOrder($order) { return $this->_currentOrder = $order; } /** * Set ordering for a group * * @param string $group * @param int $order * * @return void */ public function mockery_setGroup($group, $order) { $this->_groups[$group] = $order; } /** * Tear down tasks for this container * * @throws PHPException */ public function mockery_teardown() { try { $this->mockery_verify(); } catch (PHPException $phpException) { $this->mockery_close(); throw $phpException; } } /** * Retrieves all exceptions thrown by mocks * * @return array */ public function mockery_thrownExceptions() { /** @var array $exceptions */ $exceptions = []; foreach ($this->_mocks as $mock) { foreach ($mock->mockery_thrownExceptions() as $exception) { $exceptions[] = $exception; } } return $exceptions; } /** * Validate the current mock's ordering * * @param string $method * @param int $order * * @throws Exception */ public function mockery_validateOrder($method, $order, LegacyMockInterface $mock) { if ($order < $this->_currentOrder) { $exception = new InvalidOrderException( sprintf( 'Method %s called out of order: expected order %d, was %d', $method, $order, $this->_currentOrder ) ); $exception->setMock($mock) ->setMethodName($method) ->setExpectedOrder($order) ->setActualOrder($this->_currentOrder); throw $exception; } $this->mockery_setCurrentOrder($order); } /** * Verify the container mocks */ public function mockery_verify() { foreach ($this->_mocks as $mock) { $mock->mockery_verify(); } } /** * Store a mock and set its container reference * * @template TRememberMock of object * * @param LegacyMockInterface&MockInterface&TRememberMock $mock * * @return LegacyMockInterface&MockInterface&TRememberMock */ public function rememberMock(LegacyMockInterface $mock) { $class = get_class($mock); if (! array_key_exists($class, $this->_mocks)) { return $this->_mocks[$class] = $mock; } /** * This condition triggers for an instance mock where origin mock * is already remembered */ return $this->_mocks[] = $mock; } /** * Retrieve the last remembered mock object, * which is the same as saying retrieve the current mock being programmed where you have yet to call mock() * to change it thus why the method name is "self" since it will be used during the programming of the same mock. * * @return LegacyMockInterface|MockInterface */ public function self() { $mocks = array_values($this->_mocks); $index = count($mocks) - 1; return $mocks[$index]; } /** * @template TMock of object * @template TMixed * * @param class-string $mockName * @param null|array $constructorArgs * * @return TMock */ protected function _getInstance($mockName, $constructorArgs = null) { if ($constructorArgs !== null) { return (new ReflectionClass($mockName))->newInstanceArgs($constructorArgs); } try { $instance = $this->instantiator->instantiate($mockName); } catch (PHPException $phpException) { /** @var class-string $internalMockName */ $internalMockName = $mockName . '_Internal'; if (! class_exists($internalMockName)) { eval(sprintf( 'class %s extends %s { public function __construct() {} }', $internalMockName, $mockName )); } $instance = new $internalMockName(); } return $instance; } protected function checkForNamedMockClashes($config) { $name = $config->getName(); if ($name === null) { return; } $hash = $config->getHash(); if (array_key_exists($name, $this->_namedMocks) && $hash !== $this->_namedMocks[$name]) { throw new Exception( sprintf("The mock named '%s' has been already defined with a different mock configuration", $name) ); } $this->_namedMocks[$name] = $hash; } } PKGiZlibrary/Mockery/Exception.phpnuW+Aclosure = $closure; } /** * @return mixed */ public function __invoke() { return ($this->closure)(...func_get_args()); } } PKGiZ 6 (library/Mockery/CompositeExpectation.phpnuW+A */ protected $_expectations = []; /** * Intercept any expectation calls and direct against all expectations * * @param string $method * * @return self */ public function __call($method, array $args) { foreach ($this->_expectations as $expectation) { $expectation->{$method}(...$args); } return $this; } /** * Return the string summary of this composite expectation * * @return string */ public function __toString() { $parts = array_map(static function (ExpectationInterface $expectation): string { return (string) $expectation; }, $this->_expectations); return '[' . implode(', ', $parts) . ']'; } /** * Add an expectation to the composite * * @param ExpectationInterface|HigherOrderMessage $expectation * * @return void */ public function add($expectation) { $this->_expectations[] = $expectation; } /** * @param mixed ...$args */ public function andReturn(...$args) { return $this->__call(__FUNCTION__, $args); } /** * Set a return value, or sequential queue of return values * * @param mixed ...$args * * @return self */ public function andReturns(...$args) { return $this->andReturn(...$args); } /** * Return the parent mock of the first expectation * * @return LegacyMockInterface&MockInterface */ public function getMock() { reset($this->_expectations); $first = current($this->_expectations); return $first->getMock(); } /** * Return order number of the first expectation * * @return int */ public function getOrderNumber() { reset($this->_expectations); $first = current($this->_expectations); return $first->getOrderNumber(); } /** * Mockery API alias to getMock * * @return LegacyMockInterface&MockInterface */ public function mock() { return $this->getMock(); } /** * Starts a new expectation addition on the first mock which is the primary target outside of a demeter chain * * @param mixed ...$args * * @return Expectation */ public function shouldNotReceive(...$args) { reset($this->_expectations); $first = current($this->_expectations); return $first->getMock()->shouldNotReceive(...$args); } /** * Starts a new expectation addition on the first mock which is the primary target, outside of a demeter chain * * @param mixed ...$args * * @return Expectation */ public function shouldReceive(...$args) { reset($this->_expectations); $first = current($this->_expectations); return $first->getMock()->shouldReceive(...$args); } } PKGiZ^}^^'library/Mockery/LegacyMockInterface.phpnuW+A $args * * @return null|Expectation */ public function mockery_findExpectation($method, array $args); /** * Return the container for this mock * * @return Container */ public function mockery_getContainer(); /** * Get current ordered number * * @return int */ public function mockery_getCurrentOrder(); /** * Gets the count of expectations for this mock * * @return int */ public function mockery_getExpectationCount(); /** * Return the expectations director for the given method * * @param string $method * * @return null|ExpectationDirector */ public function mockery_getExpectationsFor($method); /** * Fetch array of ordered groups * * @return array */ public function mockery_getGroups(); /** * @return string[] */ public function mockery_getMockableMethods(); /** * @return array */ public function mockery_getMockableProperties(); /** * Return the name for this mock * * @return string */ public function mockery_getName(); /** * Alternative setup method to constructor * * @param object $partialObject * * @return void */ public function mockery_init(?Container $container = null, $partialObject = null); /** * @return bool */ public function mockery_isAnonymous(); /** * Set current ordered number * * @param int $order * * @return int */ public function mockery_setCurrentOrder($order); /** * Return the expectations director for the given method * * @param string $method * * @return null|ExpectationDirector */ public function mockery_setExpectationsFor($method, ExpectationDirector $director); /** * Set ordering for a group * * @param string $group * @param int $order * * @return void */ public function mockery_setGroup($group, $order); /** * Tear down tasks for this mock * * @return void */ public function mockery_teardown(); /** * Validate the current mock's ordering * * @param string $method * @param int $order * * @throws Exception * * @return void */ public function mockery_validateOrder($method, $order); /** * Iterate across all expectation directors and validate each * * @throws Throwable * * @return void */ public function mockery_verify(); /** * Allows additional methods to be mocked that do not explicitly exist on mocked class * * @param string $method the method name to be mocked * @return self */ public function shouldAllowMockingMethod($method); /** * @return self */ public function shouldAllowMockingProtectedMethods(); /** * Set mock to defer unexpected methods to its parent if possible * * @deprecated since 1.4.0. Please use makePartial() instead. * * @return self */ public function shouldDeferMissing(); /** * @return self */ public function shouldHaveBeenCalled(); /** * @template TMixed * @param string $method * @param null|array|Closure $args * * @return self */ public function shouldHaveReceived($method, $args = null); /** * Set mock to ignore unexpected methods and return Undefined class * * @template TReturnValue * * @param null|TReturnValue $returnValue the default return value for calls to missing functions on this mock * * @return self */ public function shouldIgnoreMissing($returnValue = null); /** * @template TMixed * @param null|array $args (optional) * * @return self */ public function shouldNotHaveBeenCalled(?array $args = null); /** * @template TMixed * @param string $method * @param null|array|Closure $args * * @return self */ public function shouldNotHaveReceived($method, $args = null); /** * Shortcut method for setting an expectation that a method should not be called. * * @param string ...$methodNames one or many methods that are expected not to be called in this mock * * @return Expectation|ExpectationInterface|HigherOrderMessage */ public function shouldNotReceive(...$methodNames); /** * Set expected method calls * * @param string ...$methodNames one or many methods that are expected to be called in this mock * * @return Expectation|ExpectationInterface|HigherOrderMessage */ public function shouldReceive(...$methodNames); } PKGiZs:ZZ+library/Mockery/VerificationExpectation.phpnuW+A_actualCount = 0; } /** * @return void */ public function clearCountValidators() { $this->_countValidators = []; } } PKGiZ!q**'library/Mockery/ExpectationDirector.phpnuW+A */ protected $_defaults = []; /** * Stores an array of all expectations for this mock * * @var list */ protected $_expectations = []; /** * The expected order of next call * * @var int */ protected $_expectedOrder = null; /** * Mock object the director is attached to * * @var LegacyMockInterface|MockInterface */ protected $_mock = null; /** * Method name the director is directing * * @var string */ protected $_name = null; /** * Constructor * * @param string $name */ public function __construct($name, LegacyMockInterface $mock) { $this->_name = $name; $this->_mock = $mock; } /** * Add a new expectation to the director */ public function addExpectation(Expectation $expectation) { $this->_expectations[] = $expectation; } /** * Handle a method call being directed by this instance * * @return mixed */ public function call(array $args) { $expectation = $this->findExpectation($args); if ($expectation !== null) { return $expectation->verifyCall($args); } $exception = new NoMatchingExpectationException( 'No matching handler found for ' . $this->_mock->mockery_getName() . '::' . Mockery::formatArgs($this->_name, $args) . '. Either the method was unexpected or its arguments matched' . ' no expected argument list for this method' . PHP_EOL . PHP_EOL . Mockery::formatObjects($args) ); $exception->setMock($this->_mock) ->setMethodName($this->_name) ->setActualArguments($args); throw $exception; } /** * Attempt to locate an expectation matching the provided args * * @return mixed */ public function findExpectation(array $args) { $expectation = null; if ($this->_expectations !== []) { $expectation = $this->_findExpectationIn($this->_expectations, $args); } if ($expectation === null && $this->_defaults !== []) { return $this->_findExpectationIn($this->_defaults, $args); } return $expectation; } /** * Return all expectations assigned to this director * * @return array */ public function getDefaultExpectations() { return $this->_defaults; } /** * Return the number of expectations assigned to this director. * * @return int */ public function getExpectationCount() { $count = 0; $expectations = $this->getExpectations(); if ($expectations === []) { $expectations = $this->getDefaultExpectations(); } foreach ($expectations as $expectation) { if ($expectation->isCallCountConstrained()) { ++$count; } } return $count; } /** * Return all expectations assigned to this director * * @return array */ public function getExpectations() { return $this->_expectations; } /** * Make the given expectation a default for all others assuming it was correctly created last * * @throws Exception * * @return void */ public function makeExpectationDefault(Expectation $expectation) { if (end($this->_expectations) === $expectation) { array_pop($this->_expectations); array_unshift($this->_defaults, $expectation); return; } throw new Exception('Cannot turn a previously defined expectation into a default'); } /** * Verify all expectations of the director * * @throws Exception * * @return void */ public function verify() { if ($this->_expectations !== []) { foreach ($this->_expectations as $expectation) { $expectation->verify(); } return; } foreach ($this->_defaults as $expectation) { $expectation->verify(); } } /** * Search current array of expectations for a match * * @param array $expectations * * @return null|ExpectationInterface */ protected function _findExpectationIn(array $expectations, array $args) { foreach ($expectations as $expectation) { if (! $expectation->isEligible()) { continue; } if (! $expectation->matchArgs($args)) { continue; } return $expectation; } foreach ($expectations as $expectation) { if ($expectation->matchArgs($args)) { return $expectation; } } return null; } } PKGiZbepp2library/Mockery/Generator/TargetClassInterface.phpnuW+A */ public function getAttributes(); /** * Returns the targetClass's interfaces. * * @return array */ public function getInterfaces(); /** * Returns the targetClass's methods. * * @return array */ public function getMethods(); /** * Returns the targetClass's name. * * @return class-string */ public function getName(); /** * Returns the targetClass's namespace name. * * @return string */ public function getNamespaceName(); /** * Returns the targetClass's short name. * * @return string */ public function getShortName(); /** * Returns whether the targetClass has * an internal ancestor. * * @return bool */ public function hasInternalAncestor(); /** * Returns whether the targetClass is in * the passed interface. * * @param class-string|string $interface * * @return bool */ public function implementsInterface($interface); /** * Returns whether the targetClass is in namespace. * * @return bool */ public function inNamespace(); /** * Returns whether the targetClass is abstract. * * @return bool */ public function isAbstract(); /** * Returns whether the targetClass is final. * * @return bool */ public function isFinal(); } PKGiZJ9 9 9library/Mockery/Generator/StringManipulationGenerator.phpnuW+A */ protected $passes = []; /** * @var string */ private $code; /** * @param list $passes */ public function __construct(array $passes) { $this->passes = $passes; $this->code = file_get_contents(__DIR__ . '/../Mock.php'); } /** * @param Pass $pass * @return void */ public function addPass(Pass $pass) { $this->passes[] = $pass; } /** * @return MockDefinition */ public function generate(MockConfiguration $config) { $className = $config->getName() ?: $config->generateName(); $namedConfig = $config->rename($className); $code = $this->code; foreach ($this->passes as $pass) { $code = $pass->apply($code, $namedConfig); } return new MockDefinition($namedConfig, $code); } /** * Creates a new StringManipulationGenerator with the default passes * * @return StringManipulationGenerator */ public static function withDefaultPasses() { return new static([ new CallTypeHintPass(), new MagicMethodTypeHintsPass(), new ClassPass(), new TraitPass(), new ClassNamePass(), new InstanceMockPass(), new InterfacePass(), new AvoidMethodClashPass(), new MethodDefinitionPass(), new RemoveUnserializeForInternalSerializableClassesPass(), new RemoveBuiltinMethodsThatAreFinalPass(), new RemoveDestructorPass(), new ConstantsPass(), new ClassAttributesPass(), ]); } } PKGiZk6library/Mockery/Generator/MockConfigurationBuilder.phpnuW+A */ protected $blackListedMethods = [ '__call', '__callStatic', '__clone', '__wakeup', '__set', '__get', '__toString', '__isset', '__destruct', '__debugInfo', ## mocking this makes it difficult to debug with xdebug // below are reserved words in PHP '__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor', ]; /** * @var array */ protected $constantsMap = []; /** * @var bool */ protected $instanceMock = false; /** * @var bool */ protected $mockOriginalDestructor = false; /** * @var string */ protected $name; /** * @var array */ protected $parameterOverrides = []; /** * @var list */ protected $php7SemiReservedKeywords = [ 'callable', 'class', 'trait', 'extends', 'implements', 'static', 'abstract', 'final', 'public', 'protected', 'private', 'const', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endwhile', 'and', 'global', 'goto', 'instanceof', 'insteadof', 'interface', 'namespace', 'new', 'or', 'xor', 'try', 'use', 'var', 'exit', 'list', 'clone', 'include', 'include_once', 'throw', 'array', 'print', 'echo', 'require', 'require_once', 'return', 'else', 'elseif', 'default', 'break', 'continue', 'switch', 'yield', 'function', 'if', 'endswitch', 'finally', 'for', 'foreach', 'declare', 'case', 'do', 'while', 'as', 'catch', 'die', 'self', 'parent', ]; /** * @var array */ protected $targets = []; /** * @var array */ protected $whiteListedMethods = []; public function __construct() { $this->blackListedMethods = array_diff($this->blackListedMethods, $this->php7SemiReservedKeywords); } /** * @param string $blackListedMethod * @return self */ public function addBlackListedMethod($blackListedMethod) { $this->blackListedMethods[] = $blackListedMethod; return $this; } /** * @param list $blackListedMethods * @return self */ public function addBlackListedMethods(array $blackListedMethods) { foreach ($blackListedMethods as $method) { $this->addBlackListedMethod($method); } return $this; } /** * @param class-string $target * @return self */ public function addTarget($target) { $this->targets[] = $target; return $this; } /** * @param list $targets * @return self */ public function addTargets($targets) { foreach ($targets as $target) { $this->addTarget($target); } return $this; } /** * @return self */ public function addWhiteListedMethod($whiteListedMethod) { $this->whiteListedMethods[] = $whiteListedMethod; return $this; } /** * @return self */ public function addWhiteListedMethods(array $whiteListedMethods) { foreach ($whiteListedMethods as $method) { $this->addWhiteListedMethod($method); } return $this; } /** * @return MockConfiguration */ public function getMockConfiguration() { return new MockConfiguration( $this->targets, $this->blackListedMethods, $this->whiteListedMethods, $this->name, $this->instanceMock, $this->parameterOverrides, $this->mockOriginalDestructor, $this->constantsMap ); } /** * @param list $blackListedMethods * @return self */ public function setBlackListedMethods(array $blackListedMethods) { $this->blackListedMethods = $blackListedMethods; return $this; } /** * @return self */ public function setConstantsMap(array $map) { $this->constantsMap = $map; return $this; } /** * @param bool $instanceMock */ public function setInstanceMock($instanceMock) { $this->instanceMock = (bool) $instanceMock; return $this; } /** * @param bool $mockDestructor */ public function setMockOriginalDestructor($mockDestructor) { $this->mockOriginalDestructor = (bool) $mockDestructor; return $this; } /** * @param string $name */ public function setName($name) { $this->name = $name; return $this; } /** * @return self */ public function setParameterOverrides(array $overrides) { $this->parameterOverrides = $overrides; return $this; } /** * @param list $whiteListedMethods * @return self */ public function setWhiteListedMethods(array $whiteListedMethods) { $this->whiteListedMethods = $whiteListedMethods; return $this; } } PKGiZPJ}Flibrary/Mockery/Generator/StringManipulation/Pass/CallTypeHintPass.phpnuW+ArequiresCallTypeHintRemoval()) { $code = str_replace( 'public function __call($method, array $args)', 'public function __call($method, $args)', $code ); } if ($config->requiresCallStaticTypeHintRemoval()) { return str_replace( 'public static function __callStatic($method, array $args)', 'public static function __callStatic($method, $args)', $code ); } return $code; } } PKGiZ4ilibrary/Mockery/Generator/StringManipulation/Pass/RemoveUnserializeForInternalSerializableClassesPass.phpnuW+AgetTargetClass(); if (! $target) { return $code; } if (! $target->hasInternalAncestor() || ! $target->implementsInterface('Serializable')) { return $code; } return $this->appendToClass( $code, PHP_VERSION_ID < 80100 ? self::DUMMY_METHOD_DEFINITION_LEGACY : self::DUMMY_METHOD_DEFINITION ); } protected function appendToClass($class, $code) { $lastBrace = strrpos($class, '}'); return substr($class, 0, $lastBrace) . $code . "\n }\n"; } } PKGiZZ?library/Mockery/Generator/StringManipulation/Pass/ClassPass.phpnuW+AgetTargetClass(); if (! $target) { return $code; } if ($target->isFinal()) { return $code; } $className = ltrim($target->getName(), '\\'); if (! class_exists($className)) { Mockery::declareClass($className); } return str_replace( 'implements MockInterface', 'extends \\' . $className . ' implements MockInterface', $code ); } } PKGiZ]Ilibrary/Mockery/Generator/StringManipulation/Pass/ClassAttributesPass.phpnuW+AgetTargetClass(); if (! $class) { return $code; } /** @var array $attributes */ $attributes = $class->getAttributes(); if ($attributes !== []) { return str_replace('#[\AllowDynamicProperties]', '#[' . implode(',', $attributes) . ']', $code); } return $code; } } PKGiZ_ZƗ Flibrary/Mockery/Generator/StringManipulation/Pass/InstanceMockPass.phpnuW+A_mockery_ignoreVerification = false; \$associatedRealObject = \Mockery::fetchMock(__CLASS__); foreach (get_object_vars(\$this) as \$attr => \$val) { if (\$attr !== "_mockery_ignoreVerification" && \$attr !== "_mockery_expectations") { \$this->\$attr = \$associatedRealObject->\$attr; } } \$directors = \$associatedRealObject->mockery_getExpectations(); foreach (\$directors as \$method=>\$director) { // get the director method needed \$existingDirector = \$this->mockery_getExpectationsFor(\$method); if (!\$existingDirector) { \$existingDirector = new \Mockery\ExpectationDirector(\$method, \$this); \$this->mockery_setExpectationsFor(\$method, \$existingDirector); } \$expectations = \$director->getExpectations(); foreach (\$expectations as \$expectation) { \$clonedExpectation = clone \$expectation; \$existingDirector->addExpectation(\$clonedExpectation); } \$defaultExpectations = \$director->getDefaultExpectations(); foreach (array_reverse(\$defaultExpectations) as \$expectation) { \$clonedExpectation = clone \$expectation; \$existingDirector->addExpectation(\$clonedExpectation); \$existingDirector->makeExpectationDefault(\$clonedExpectation); } } \Mockery::getContainer()->rememberMock(\$this); \$this->_mockery_constructorCalled(func_get_args()); } MOCK; /** * @param string $code * @return string */ public function apply($code, MockConfiguration $config) { if ($config->isInstanceMock()) { return $this->appendToClass($code, static::INSTANCE_MOCK_CODE); } return $code; } protected function appendToClass($class, $code) { $lastBrace = strrpos($class, '}'); return substr($class, 0, $lastBrace) . $code . "\n }\n"; } } PKGiZșbbNlibrary/Mockery/Generator/StringManipulation/Pass/MagicMethodTypeHintsPass.phpnuW+AgetMagicMethods($config->getTargetClass()); foreach ($config->getTargetInterfaces() as $interface) { $magicMethods = array_merge($magicMethods, $this->getMagicMethods($interface)); } foreach ($magicMethods as $method) { $code = $this->applyMagicTypeHints($code, $method); } return $code; } /** * Returns the magic methods within the * passed DefinedTargetClass. * * @return array */ public function getMagicMethods(?TargetClassInterface $class = null) { if (! $class instanceof TargetClassInterface) { return []; } return array_filter($class->getMethods(), function (Method $method) { return in_array($method->getName(), $this->mockMagicMethods, true); }); } protected function renderTypeHint(Parameter $param) { $typeHint = $param->getTypeHint(); return $typeHint === null ? '' : sprintf('%s ', $typeHint); } /** * Applies type hints of magic methods from * class to the passed code. * * @param int $code * * @return string */ private function applyMagicTypeHints($code, Method $method) { if ($this->isMethodWithinCode($code, $method)) { $namedParameters = $this->getOriginalParameters($code, $method); $code = preg_replace( $this->getDeclarationRegex($method->getName()), $this->getMethodDeclaration($method, $namedParameters), $code ); } return $code; } /** * Returns a regex string used to match the * declaration of some method. * * @param string $methodName * * @return string */ private function getDeclarationRegex($methodName) { return sprintf('/public\s+(?:static\s+)?function\s+%s\s*\(.*\)\s*(?=\{)/i', $methodName); } /** * Gets the declaration code, as a string, for the passed method. * * @param array $namedParameters * * @return string */ private function getMethodDeclaration(Method $method, array $namedParameters) { $declaration = 'public'; $declaration .= $method->isStatic() ? ' static' : ''; $declaration .= ' function ' . $method->getName() . '('; foreach ($method->getParameters() as $index => $parameter) { $declaration .= $this->renderTypeHint($parameter); $name = $namedParameters[$index] ?? $parameter->getName(); $declaration .= '$' . $name; $declaration .= ','; } $declaration = rtrim($declaration, ','); $declaration .= ') '; $returnType = $method->getReturnType(); if ($returnType !== null) { $declaration .= sprintf(': %s', $returnType); } return $declaration; } /** * Returns the method original parameters, as they're * described in the $code string. * * @param int $code * * @return array */ private function getOriginalParameters($code, Method $method) { $matches = []; $parameterMatches = []; preg_match($this->getDeclarationRegex($method->getName()), $code, $matches); if ($matches !== []) { preg_match_all('/(?<=\$)(\w+)+/i', $matches[0], $parameterMatches); } $groupMatches = end($parameterMatches); return is_array($groupMatches) ? $groupMatches : [$groupMatches]; } /** * Checks if the method is declared within code. * * @param int $code * * @return bool */ private function isMethodWithinCode($code, Method $method) { return preg_match($this->getDeclarationRegex($method->getName()), $code) === 1; } } PKGiZw@f,Clibrary/Mockery/Generator/StringManipulation/Pass/ClassNamePass.phpnuW+AgetNamespaceName(); $namespace = ltrim($namespace, '\\'); $className = $config->getShortName(); $code = str_replace('namespace Mockery;', $namespace !== '' ? 'namespace ' . $namespace . ';' : '', $code); return str_replace('class Mock', 'class ' . $className, $code); } } PKGiZv??Zlibrary/Mockery/Generator/StringManipulation/Pass/RemoveBuiltinMethodsThatAreFinalPass.phpnuW+A '/public function __wakeup\(\)\s+\{.*?\}/sm', '__toString' => '/public function __toString\(\)\s+(:\s+string)?\s*\{.*?\}/sm', ]; /** * @param string $code * @return string */ public function apply($code, MockConfiguration $config) { $target = $config->getTargetClass(); if (! $target instanceof TargetClassInterface) { return $code; } foreach ($target->getMethods() as $method) { if (! $method->isFinal()) { continue; } if (! isset($this->methods[$method->getName()])) { continue; } $code = preg_replace($this->methods[$method->getName()], '', $code); } return $code; } } PKGiZ2ުClibrary/Mockery/Generator/StringManipulation/Pass/InterfacePass.phpnuW+AgetTargetInterfaces() as $i) { $name = ltrim($i->getName(), '\\'); if (! interface_exists($name)) { Mockery::declareInterface($name); } } $interfaces = array_reduce($config->getTargetInterfaces(), static function ($code, $i) { return $code . ', \\' . ltrim($i->getName(), '\\'); }, ''); return str_replace('implements MockInterface', 'implements MockInterface' . $interfaces, $code); } } PKGiZ?library/Mockery/Generator/StringManipulation/Pass/TraitPass.phpnuW+AgetTargetTraits(); if ($traits === []) { return $code; } $useStatements = array_map(static function ($trait) { return 'use \\\\' . ltrim($trait->getName(), '\\') . ';'; }, $traits); return preg_replace('/^{$/m', "{\n " . implode("\n ", $useStatements) . "\n", $code); } } PKGiZsoJlibrary/Mockery/Generator/StringManipulation/Pass/AvoidMethodClashPass.phpnuW+AgetName(); }, $config->getMethodsToMock()); foreach (['allows', 'expects'] as $method) { if (in_array($method, $names, true)) { $code = preg_replace(sprintf('#// start method %s.*// end method %s#ms', $method, $method), '', $code); $code = str_replace(' implements MockInterface', ' implements LegacyMockInterface', $code); } } return $code; } } PKGiZQ--Clibrary/Mockery/Generator/StringManipulation/Pass/ConstantsPass.phpnuW+AgetConstantsMap(); if ($cm === []) { return $code; } $name = $config->getName(); if (! array_key_exists($name, $cm)) { return $code; } $constantsCode = ''; foreach ($cm[$name] as $constant => $value) { $constantsCode .= sprintf("\n const %s = %s;\n", $constant, var_export($value, true)); } $offset = strrpos($code, '}'); if ($offset === false) { return $code; } return substr_replace($code, $constantsCode, $offset) . '}' . PHP_EOL; } } PKGiZ>vJlibrary/Mockery/Generator/StringManipulation/Pass/MethodDefinitionPass.phpnuW+AgetMethodsToMock() as $method) { if ($method->isPublic()) { $methodDef = 'public'; } elseif ($method->isProtected()) { $methodDef = 'protected'; } else { $methodDef = 'private'; } if ($method->isStatic()) { $methodDef .= ' static'; } $methodDef .= ' function '; $methodDef .= $method->returnsReference() ? ' & ' : ''; $methodDef .= $method->getName(); $methodDef .= $this->renderParams($method, $config); $methodDef .= $this->renderReturnType($method); $methodDef .= $this->renderMethodBody($method, $config); $code = $this->appendToClass($code, $methodDef); } return $code; } protected function appendToClass($class, $code) { $lastBrace = strrpos($class, '}'); return substr($class, 0, $lastBrace) . $code . "\n }\n"; } protected function renderParams(Method $method, $config) { $class = $method->getDeclaringClass(); if ($class->isInternal()) { $overrides = $config->getParameterOverrides(); if (isset($overrides[strtolower($class->getName())][$method->getName()])) { return '(' . implode(',', $overrides[strtolower($class->getName())][$method->getName()]) . ')'; } } $methodParams = []; $params = $method->getParameters(); $isPhp81 = PHP_VERSION_ID >= 80100; foreach ($params as $param) { $paramDef = $this->renderTypeHint($param); $paramDef .= $param->isPassedByReference() ? '&' : ''; $paramDef .= $param->isVariadic() ? '...' : ''; $paramDef .= '$' . $param->getName(); if (! $param->isVariadic()) { if ($param->isDefaultValueAvailable() !== false) { $defaultValue = $param->getDefaultValue(); if (is_object($defaultValue)) { $prefix = get_class($defaultValue); if ($isPhp81) { if (enum_exists($prefix)) { $prefix = var_export($defaultValue, true); } elseif ( ! $param->isDefaultValueConstant() && // "Parameter #1 [ F\Q\CN $a = new \F\Q\CN(param1, param2: 2) ] preg_match( '#\s.*?\s=\snew\s(.*?)\s]$#', $param->__toString(), $matches ) === 1 ) { $prefix = 'new ' . $matches[1]; } } } else { $prefix = var_export($defaultValue, true); } $paramDef .= ' = ' . $prefix; } elseif ($param->isOptional()) { $paramDef .= ' = null'; } } $methodParams[] = $paramDef; } return '(' . implode(', ', $methodParams) . ')'; } protected function renderReturnType(Method $method) { $type = $method->getReturnType(); return $type ? sprintf(': %s', $type) : ''; } protected function renderTypeHint(Parameter $param) { $typeHint = $param->getTypeHint(); return $typeHint === null ? '' : sprintf('%s ', $typeHint); } private function renderMethodBody($method, $config) { $invoke = $method->isStatic() ? 'static::_mockery_handleStaticMethodCall' : '$this->_mockery_handleMethodCall'; $body = <<getDeclaringClass(); $class_name = strtolower($class->getName()); $overrides = $config->getParameterOverrides(); if (isset($overrides[$class_name][$method->getName()])) { $params = array_values($overrides[$class_name][$method->getName()]); $paramCount = count($params); for ($i = 0; $i < $paramCount; ++$i) { $param = $params[$i]; if (strpos($param, '&') !== false) { $body .= << {$i}) { \$argv[{$i}] = {$param}; } BODY; } } } else { $params = array_values($method->getParameters()); $paramCount = count($params); for ($i = 0; $i < $paramCount; ++$i) { $param = $params[$i]; if (! $param->isPassedByReference()) { continue; } $body .= << {$i}) { \$argv[{$i}] =& \${$param->getName()}; } BODY; } } $body .= "\$ret = {$invoke}(__FUNCTION__, \$argv);\n"; if (! in_array($method->getReturnType(), ['never', 'void'], true)) { $body .= "return \$ret;\n"; } return $body . "}\n"; } } PKGiZ^~Jlibrary/Mockery/Generator/StringManipulation/Pass/RemoveDestructorPass.phpnuW+AgetTargetClass(); if (! $target) { return $code; } if (! $config->isMockOriginalDestructor()) { return preg_replace('/public function __destruct\(\)\s+\{.*?\}/sm', '', $code); } return $code; } } PKGiZ0a:library/Mockery/Generator/StringManipulation/Pass/Pass.phpnuW+A */ protected $cache = []; /** * @var Generator */ protected $generator; public function __construct(Generator $generator) { $this->generator = $generator; } /** * @return string */ public function generate(MockConfiguration $config) { $hash = $config->getHash(); if (array_key_exists($hash, $this->cache)) { return $this->cache[$hash]; } return $this->cache[$hash] = $this->generator->generate($config); } } PKGiZ—0library/Mockery/Generator/DefinedTargetClass.phpnuW+Arfc = $rfc; $this->name = $alias ?? $rfc->getName(); } /** * @return class-string */ public function __toString() { return $this->name; } /** * @param class-string $name * @param class-string|null $alias * @return self */ public static function factory($name, $alias = null) { return new self(new ReflectionClass($name), $alias); } /** * @return list */ public function getAttributes() { if (PHP_VERSION_ID < 80000) { return []; } return array_unique( array_merge( ['\AllowDynamicProperties'], array_map( static function (ReflectionAttribute $attribute): string { return '\\' . $attribute->getName(); }, $this->rfc->getAttributes() ) ) ); } /** * @return array */ public function getInterfaces() { return array_map( static function (ReflectionClass $interface): self { return new self($interface); }, $this->rfc->getInterfaces() ); } /** * @return list */ public function getMethods() { return array_map( static function (ReflectionMethod $method): Method { return new Method($method); }, $this->rfc->getMethods() ); } /** * @return class-string */ public function getName() { return $this->name; } /** * @return string */ public function getNamespaceName() { return $this->rfc->getNamespaceName(); } /** * @return string */ public function getShortName() { return $this->rfc->getShortName(); } /** * @return bool */ public function hasInternalAncestor() { if ($this->rfc->isInternal()) { return true; } $child = $this->rfc; while ($parent = $child->getParentClass()) { if ($parent->isInternal()) { return true; } $child = $parent; } return false; } /** * @param class-string $interface * @return bool */ public function implementsInterface($interface) { return $this->rfc->implementsInterface($interface); } /** * @return bool */ public function inNamespace() { return $this->rfc->inNamespace(); } /** * @return bool */ public function isAbstract() { return $this->rfc->isAbstract(); } /** * @return bool */ public function isFinal() { return $this->rfc->isFinal(); } } PKGiZ4'library/Mockery/Generator/Generator.phpnuW+Arfp = $rfp; } /** * Proxy all method calls to the reflection parameter. * * @template TMixed * @template TResult * * @param string $method * @param array $args * * @return TResult */ public function __call($method, array $args) { /** @var TResult */ return $this->rfp->{$method}(...$args); } /** * Get the reflection class for the parameter type, if it exists. * * This will be null if there was no type, or it was a scalar or a union. * * @return null|ReflectionClass * * @deprecated since 1.3.3 and will be removed in 2.0. */ public function getClass() { $typeHint = Reflector::getTypeHint($this->rfp, true); return class_exists($typeHint) ? DefinedTargetClass::factory($typeHint, false) : null; } /** * Get the name of the parameter. * * Some internal classes have funny looking definitions! * * @return string */ public function getName() { $name = $this->rfp->getName(); if (! $name || $name === '...') { return 'arg' . self::$parameterCounter++; } return $name; } /** * Get the string representation for the paramater type. * * @return null|string */ public function getTypeHint() { return Reflector::getTypeHint($this->rfp); } /** * Get the string representation for the paramater type. * * @return string * * @deprecated since 1.3.2 and will be removed in 2.0. Use getTypeHint() instead. */ public function getTypeHintAsString() { return (string) Reflector::getTypeHint($this->rfp, true); } /** * Determine if the parameter is an array. * * @return bool */ public function isArray() { return Reflector::isArray($this->rfp); } /** * Determine if the parameter is variadic. * * @return bool */ public function isVariadic() { return $this->rfp->isVariadic(); } } PKGiZ_GJJ$library/Mockery/Generator/Method.phpnuW+Amethod = $method; } /** * @template TArgs * @template TMixed * * @param string $method * @param array $args * * @return TMixed */ public function __call($method, $args) { /** @var TMixed */ return $this->method->{$method}(...$args); } /** * @return list */ public function getParameters() { return array_map(static function (ReflectionParameter $parameter) { return new Parameter($parameter); }, $this->method->getParameters()); } /** * @return null|string */ public function getReturnType() { return Reflector::getReturnType($this->method); } } PKGiZo>a-library/Mockery/Generator/MockNameBuilder.phpnuW+A */ protected $parts = []; /** * @param string $part */ public function addPart($part) { $this->parts[] = $part; return $this; } /** * @return string */ public function build() { $parts = ['Mockery', static::$mockCounter++]; foreach ($this->parts as $part) { $parts[] = str_replace('\\', '_', $part); } return implode('_', $parts); } } PKGiZ~uKuK/library/Mockery/Generator/MockConfiguration.phpnuW+A */ protected $allMethods = []; /** * Methods that should specifically not be mocked * * This is currently populated with stuff we don't know how to deal with, should really be somewhere else */ protected $blackListedMethods = []; protected $constantsMap = []; /** * An instance mock is where we override the original class before it's autoloaded * * @var bool */ protected $instanceMock = false; /** * If true, overrides original class destructor * * @var bool */ protected $mockOriginalDestructor = false; /** * The class name we'd like to use for a generated mock * * @var string|null */ protected $name; /** * Param overrides * * @var array */ protected $parameterOverrides = []; /** * A class that we'd like to mock * @var TargetClassInterface|null */ protected $targetClass; /** * @var class-string|null */ protected $targetClassName; /** * @var array */ protected $targetInterfaceNames = []; /** * A number of interfaces we'd like to mock, keyed by name to attempt to keep unique * * @var array */ protected $targetInterfaces = []; /** * An object we'd like our mock to proxy to * * @var object|null */ protected $targetObject; /** * @var array */ protected $targetTraitNames = []; /** * A number of traits we'd like to mock, keyed by name to attempt to keep unique * * @var array */ protected $targetTraits = []; /** * If not empty, only these methods will be mocked * * @var array */ protected $whiteListedMethods = []; /** * @param array $targets * @param array $blackListedMethods * @param array $whiteListedMethods * @param string|null $name * @param bool $instanceMock * @param array $parameterOverrides * @param bool $mockOriginalDestructor * @param array|scalar> $constantsMap */ public function __construct( array $targets = [], array $blackListedMethods = [], array $whiteListedMethods = [], $name = null, $instanceMock = false, array $parameterOverrides = [], $mockOriginalDestructor = false, array $constantsMap = [] ) { $this->addTargets($targets); $this->blackListedMethods = $blackListedMethods; $this->whiteListedMethods = $whiteListedMethods; $this->name = $name; $this->instanceMock = $instanceMock; $this->parameterOverrides = $parameterOverrides; $this->mockOriginalDestructor = $mockOriginalDestructor; $this->constantsMap = $constantsMap; } /** * Generate a suitable name based on the config * * @return string */ public function generateName() { $nameBuilder = new MockNameBuilder(); $targetObject = $this->getTargetObject(); if ($targetObject !== null) { $className = get_class($targetObject); $nameBuilder->addPart(strpos($className, '@') !== false ? md5($className) : $className); } $targetClass = $this->getTargetClass(); if ($targetClass instanceof TargetClassInterface) { $className = $targetClass->getName(); $nameBuilder->addPart(strpos($className, '@') !== false ? md5($className) : $className); } foreach ($this->getTargetInterfaces() as $targetInterface) { $nameBuilder->addPart($targetInterface->getName()); } return $nameBuilder->build(); } /** * @return array */ public function getBlackListedMethods() { return $this->blackListedMethods; } /** * @return array> */ public function getConstantsMap() { return $this->constantsMap; } /** * Attempt to create a hash of the configuration, in order to allow caching * * @TODO workout if this will work * * @return string */ public function getHash() { $vars = [ 'targetClassName' => $this->targetClassName, 'targetInterfaceNames' => $this->targetInterfaceNames, 'targetTraitNames' => $this->targetTraitNames, 'name' => $this->name, 'blackListedMethods' => $this->blackListedMethods, 'whiteListedMethod' => $this->whiteListedMethods, 'instanceMock' => $this->instanceMock, 'parameterOverrides' => $this->parameterOverrides, 'mockOriginalDestructor' => $this->mockOriginalDestructor, ]; return md5(serialize($vars)); } /** * Gets a list of methods from the classes, interfaces and objects and filters them appropriately. * Lot's of filtering going on, perhaps we could have filter classes to iterate through * * @return list */ public function getMethodsToMock() { $methods = $this->getAllMethods(); foreach ($methods as $key => $method) { if ($method->isFinal()) { unset($methods[$key]); } } /** * Whitelist trumps everything else */ $whiteListedMethods = $this->getWhiteListedMethods(); if ($whiteListedMethods !== []) { $whitelist = array_map('strtolower', $whiteListedMethods); return array_filter($methods, static function ($method) use ($whitelist) { if ($method->isAbstract()) { return true; } return in_array(strtolower($method->getName()), $whitelist, true); }); } /** * Remove blacklisted methods */ $blackListedMethods = $this->getBlackListedMethods(); if ($blackListedMethods !== []) { $blacklist = array_map('strtolower', $blackListedMethods); $methods = array_filter($methods, static function ($method) use ($blacklist) { return ! in_array(strtolower($method->getName()), $blacklist, true); }); } /** * Internal objects can not be instantiated with newInstanceArgs and if * they implement Serializable, unserialize will have to be called. As * such, we can't mock it and will need a pass to add a dummy * implementation */ $targetClass = $this->getTargetClass(); if ( $targetClass !== null && $targetClass->implementsInterface(Serializable::class) && $targetClass->hasInternalAncestor() ) { $methods = array_filter($methods, static function ($method) { return $method->getName() !== 'unserialize'; }); } return array_values($methods); } /** * @return string|null */ public function getName() { return $this->name; } /** * @return string */ public function getNamespaceName() { $parts = explode('\\', $this->getName()); array_pop($parts); if ($parts !== []) { return implode('\\', $parts); } return ''; } /** * @return array */ public function getParameterOverrides() { return $this->parameterOverrides; } /** * @return string */ public function getShortName() { $parts = explode('\\', $this->getName()); return array_pop($parts); } /** * @return null|TargetClassInterface */ public function getTargetClass() { if ($this->targetClass) { return $this->targetClass; } if (! $this->targetClassName) { return null; } if (class_exists($this->targetClassName)) { $alias = null; if (strpos($this->targetClassName, '@') !== false) { $alias = (new MockNameBuilder()) ->addPart('anonymous_class') ->addPart(md5($this->targetClassName)) ->build(); class_alias($this->targetClassName, $alias); } $dtc = DefinedTargetClass::factory($this->targetClassName, $alias); if ($this->getTargetObject() === null && $dtc->isFinal()) { throw new Exception( 'The class ' . $this->targetClassName . ' is marked final and its methods' . ' cannot be replaced. Classes marked final can be passed in' . ' to \Mockery::mock() as instantiated objects to create a' . ' partial mock, but only if the mock is not subject to type' . ' hinting checks.' ); } $this->targetClass = $dtc; } else { $this->targetClass = UndefinedTargetClass::factory($this->targetClassName); } return $this->targetClass; } /** * @return class-string|null */ public function getTargetClassName() { return $this->targetClassName; } /** * @return list */ public function getTargetInterfaces() { if ($this->targetInterfaces !== []) { return $this->targetInterfaces; } foreach ($this->targetInterfaceNames as $targetInterface) { if (! interface_exists($targetInterface)) { $this->targetInterfaces[] = UndefinedTargetClass::factory($targetInterface); continue; } $dtc = DefinedTargetClass::factory($targetInterface); $extendedInterfaces = array_keys($dtc->getInterfaces()); $extendedInterfaces[] = $targetInterface; $traversableFound = false; $iteratorShiftedToFront = false; foreach ($extendedInterfaces as $interface) { if (! $traversableFound && preg_match('/^\\?Iterator(|Aggregate)$/i', $interface)) { break; } if (preg_match('/^\\\\?IteratorAggregate$/i', $interface)) { $this->targetInterfaces[] = DefinedTargetClass::factory('\\IteratorAggregate'); $iteratorShiftedToFront = true; continue; } if (preg_match('/^\\\\?Iterator$/i', $interface)) { $this->targetInterfaces[] = DefinedTargetClass::factory('\\Iterator'); $iteratorShiftedToFront = true; continue; } if (preg_match('/^\\\\?Traversable$/i', $interface)) { $traversableFound = true; } } if ($traversableFound && ! $iteratorShiftedToFront) { $this->targetInterfaces[] = DefinedTargetClass::factory('\\IteratorAggregate'); } /** * We never straight up implement Traversable */ $isTraversable = preg_match('/^\\\\?Traversable$/i', $targetInterface); if ($isTraversable === 0 || $isTraversable === false) { $this->targetInterfaces[] = $dtc; } } return $this->targetInterfaces = array_unique($this->targetInterfaces); } /** * @return object|null */ public function getTargetObject() { return $this->targetObject; } /** * @return list */ public function getTargetTraits() { if ($this->targetTraits !== []) { return $this->targetTraits; } foreach ($this->targetTraitNames as $targetTrait) { $this->targetTraits[] = DefinedTargetClass::factory($targetTrait); } $this->targetTraits = array_unique($this->targetTraits); // just in case return $this->targetTraits; } /** * @return array */ public function getWhiteListedMethods() { return $this->whiteListedMethods; } /** * @return bool */ public function isInstanceMock() { return $this->instanceMock; } /** * @return bool */ public function isMockOriginalDestructor() { return $this->mockOriginalDestructor; } /** * @param class-string $className * @return self */ public function rename($className) { $targets = []; if ($this->targetClassName) { $targets[] = $this->targetClassName; } if ($this->targetInterfaceNames) { $targets = array_merge($targets, $this->targetInterfaceNames); } if ($this->targetTraitNames) { $targets = array_merge($targets, $this->targetTraitNames); } if ($this->targetObject) { $targets[] = $this->targetObject; } return new self( $targets, $this->blackListedMethods, $this->whiteListedMethods, $className, $this->instanceMock, $this->parameterOverrides, $this->mockOriginalDestructor, $this->constantsMap ); } /** * We declare the __callStatic method to handle undefined stuff, if the class * we're mocking has also defined it, we need to comply with their interface * * @return bool */ public function requiresCallStaticTypeHintRemoval() { foreach ($this->getAllMethods() as $method) { if ($method->getName() === '__callStatic') { $params = $method->getParameters(); if (! array_key_exists(1, $params)) { return false; } return ! $params[1]->isArray(); } } return false; } /** * We declare the __call method to handle undefined stuff, if the class * we're mocking has also defined it, we need to comply with their interface * * @return bool */ public function requiresCallTypeHintRemoval() { foreach ($this->getAllMethods() as $method) { if ($method->getName() === '__call') { $params = $method->getParameters(); return ! $params[1]->isArray(); } } return false; } /** * @param class-string|object $target */ protected function addTarget($target) { if (is_object($target)) { $this->setTargetObject($target); $this->setTargetClassName(get_class($target)); return; } if ($target[0] !== '\\') { $target = '\\' . $target; } if (class_exists($target)) { $this->setTargetClassName($target); return; } if (interface_exists($target)) { $this->addTargetInterfaceName($target); return; } if (trait_exists($target)) { $this->addTargetTraitName($target); return; } /** * Default is to set as class, or interface if class already set * * Don't like this condition, can't remember what the default * targetClass is for */ if ($this->getTargetClassName()) { $this->addTargetInterfaceName($target); return; } $this->setTargetClassName($target); } /** * If we attempt to implement Traversable, * we must ensure we are also implementing either Iterator or IteratorAggregate, * and that whichever one it is comes before Traversable in the list of implements. * * @param class-string $targetInterface */ protected function addTargetInterfaceName($targetInterface) { $this->targetInterfaceNames[] = $targetInterface; } /** * @param array $interfaces */ protected function addTargets($interfaces) { foreach ($interfaces as $interface) { $this->addTarget($interface); } } /** * @param class-string $targetTraitName */ protected function addTargetTraitName($targetTraitName) { $this->targetTraitNames[] = $targetTraitName; } /** * @return list */ protected function getAllMethods() { if ($this->allMethods) { return $this->allMethods; } $classes = $this->getTargetInterfaces(); if ($this->getTargetClass()) { $classes[] = $this->getTargetClass(); } $methods = []; foreach ($classes as $class) { $methods = array_merge($methods, $class->getMethods()); } foreach ($this->getTargetTraits() as $trait) { foreach ($trait->getMethods() as $method) { if ($method->isAbstract()) { $methods[] = $method; } } } $names = []; $methods = array_filter($methods, static function ($method) use (&$names) { if (in_array($method->getName(), $names, true)) { return false; } $names[] = $method->getName(); return true; }); return $this->allMethods = $methods; } /** * @param class-string $targetClassName */ protected function setTargetClassName($targetClassName) { $this->targetClassName = $targetClassName; } /** * @param object $object */ protected function setTargetObject($object) { $this->targetObject = $object; } } PKGiZ *,library/Mockery/Generator/MockDefinition.phpnuW+AgetName()) { throw new InvalidArgumentException('MockConfiguration must contain a name'); } $this->config = $config; $this->code = $code; } /** * @return string */ public function getClassName() { return $this->config->getName(); } /** * @return string */ public function getCode() { return $this->code; } /** * @return MockConfiguration */ public function getConfig() { return $this->config; } } PKGiZ cZ Z 2library/Mockery/Generator/UndefinedTargetClass.phpnuW+Aname = $name; } /** * @return class-string */ public function __toString() { return $this->name; } /** * @param class-string $name * @return self */ public static function factory($name) { return new self($name); } /** * @return list */ public function getAttributes() { return []; } /** * @return list */ public function getInterfaces() { return []; } /** * @return list */ public function getMethods() { return []; } /** * @return class-string */ public function getName() { return $this->name; } /** * @return string */ public function getNamespaceName() { $parts = explode('\\', ltrim($this->getName(), '\\')); array_pop($parts); return implode('\\', $parts); } /** * @return string */ public function getShortName() { $parts = explode('\\', $this->getName()); return array_pop($parts); } /** * @return bool */ public function hasInternalAncestor() { return false; } /** * @param class-string $interface * @return bool */ public function implementsInterface($interface) { return false; } /** * @return bool */ public function inNamespace() { return $this->getNamespaceName() !== ''; } /** * @return bool */ public function isAbstract() { return false; } /** * @return bool */ public function isFinal() { return false; } } PKGiZw](library/Mockery/ExpectationInterface.phpnuW+A */ public const BUILTIN_TYPES = ['array', 'bool', 'int', 'float', 'null', 'object', 'string']; /** * List of reserved words. * * @var list */ public const RESERVED_WORDS = ['bool', 'true', 'false', 'float', 'int', 'iterable', 'mixed', 'never', 'null', 'object', 'string', 'void']; /** * Iterable. * * @var list */ private const ITERABLE = ['iterable']; /** * Traversable array. * * @var list */ private const TRAVERSABLE_ARRAY = ['\Traversable', 'array']; /** * Compute the string representation for the return type. * * @param bool $withoutNullable * * @return null|string */ public static function getReturnType(ReflectionMethod $method, $withoutNullable = false) { $type = $method->getReturnType(); if (! $type instanceof ReflectionType && method_exists($method, 'getTentativeReturnType')) { $type = $method->getTentativeReturnType(); } if (! $type instanceof ReflectionType) { return null; } $typeHint = self::getTypeFromReflectionType($type, $method->getDeclaringClass()); return (! $withoutNullable && $type->allowsNull()) ? self::formatNullableType($typeHint) : $typeHint; } /** * Compute the string representation for the simplest return type. * * @return null|string */ public static function getSimplestReturnType(ReflectionMethod $method) { $type = $method->getReturnType(); if (! $type instanceof ReflectionType && method_exists($method, 'getTentativeReturnType')) { $type = $method->getTentativeReturnType(); } if (! $type instanceof ReflectionType || $type->allowsNull()) { return null; } $typeInformation = self::getTypeInformation($type, $method->getDeclaringClass()); // return the first primitive type hint foreach ($typeInformation as $info) { if ($info['isPrimitive']) { return $info['typeHint']; } } // if no primitive type, return the first type foreach ($typeInformation as $info) { return $info['typeHint']; } return null; } /** * Compute the string representation for the paramater type. * * @param bool $withoutNullable * * @return null|string */ public static function getTypeHint(ReflectionParameter $param, $withoutNullable = false) { if (! $param->hasType()) { return null; } $type = $param->getType(); $declaringClass = $param->getDeclaringClass(); $typeHint = self::getTypeFromReflectionType($type, $declaringClass); return (! $withoutNullable && $type->allowsNull()) ? self::formatNullableType($typeHint) : $typeHint; } /** * Determine if the parameter is typed as an array. * * @return bool */ public static function isArray(ReflectionParameter $param) { $type = $param->getType(); return $type instanceof ReflectionNamedType && $type->getName(); } /** * Determine if the given type is a reserved word. */ public static function isReservedWord(string $type): bool { return in_array(strtolower($type), self::RESERVED_WORDS, true); } /** * Format the given type as a nullable type. */ private static function formatNullableType(string $typeHint): string { if ($typeHint === 'mixed') { return $typeHint; } if (strpos($typeHint, 'null') !== false) { return $typeHint; } if (PHP_VERSION_ID < 80000) { return sprintf('?%s', $typeHint); } return sprintf('%s|null', $typeHint); } private static function getTypeFromReflectionType(ReflectionType $type, ReflectionClass $declaringClass): string { if ($type instanceof ReflectionNamedType) { $typeHint = $type->getName(); if ($type->isBuiltin()) { return $typeHint; } if ($typeHint === 'static') { return $typeHint; } // 'self' needs to be resolved to the name of the declaring class if ($typeHint === 'self') { $typeHint = $declaringClass->getName(); } // 'parent' needs to be resolved to the name of the parent class if ($typeHint === 'parent') { $typeHint = $declaringClass->getParentClass()->getName(); } // class names need prefixing with a slash return sprintf('\\%s', $typeHint); } if ($type instanceof ReflectionIntersectionType) { $types = array_map( static function (ReflectionType $type) use ($declaringClass): string { return self::getTypeFromReflectionType($type, $declaringClass); }, $type->getTypes() ); return implode('&', $types); } if ($type instanceof ReflectionUnionType) { $types = array_map( static function (ReflectionType $type) use ($declaringClass): string { return self::getTypeFromReflectionType($type, $declaringClass); }, $type->getTypes() ); $intersect = array_intersect(self::TRAVERSABLE_ARRAY, $types); if ($intersect === self::TRAVERSABLE_ARRAY) { $types = array_merge(self::ITERABLE, array_diff($types, self::TRAVERSABLE_ARRAY)); } return implode( '|', array_map( static function (string $type): string { return strpos($type, '&') === false ? $type : sprintf('(%s)', $type); }, $types ) ); } throw new InvalidArgumentException('Unknown ReflectionType: ' . get_debug_type($type)); } /** * Get the string representation of the given type. * * @return list */ private static function getTypeInformation(ReflectionType $type, ReflectionClass $declaringClass): array { // PHP 8 union types and PHP 8.1 intersection types can be recursively processed if ($type instanceof ReflectionUnionType || $type instanceof ReflectionIntersectionType) { $types = []; foreach ($type->getTypes() as $innterType) { foreach (self::getTypeInformation($innterType, $declaringClass) as $info) { if ($info['typeHint'] === 'null' && $info['isPrimitive']) { continue; } $types[] = $info; } } return $types; } // $type must be an instance of \ReflectionNamedType $typeHint = $type->getName(); // builtins can be returned as is if ($type->isBuiltin()) { return [ [ 'typeHint' => $typeHint, 'isPrimitive' => in_array($typeHint, self::BUILTIN_TYPES, true), ], ]; } // 'static' can be returned as is if ($typeHint === 'static') { return [ [ 'typeHint' => $typeHint, 'isPrimitive' => false, ], ]; } // 'self' needs to be resolved to the name of the declaring class if ($typeHint === 'self') { $typeHint = $declaringClass->getName(); } // 'parent' needs to be resolved to the name of the parent class if ($typeHint === 'parent') { $typeHint = $declaringClass->getParentClass()->getName(); } // class names need prefixing with a slash return [ [ 'typeHint' => sprintf('\\%s', $typeHint), 'isPrimitive' => false, ], ]; } } PKGiZeA800library/Mockery/Undefined.phpnuW+Amethod = $method; $this->args = $args; } /** * @return array */ public function getArgs() { return $this->args; } /** * @return string */ public function getMethod() { return $this->method; } } PKGiZ1library/Mockery/QuickDefinitionsConfiguration.phpnuW+A_quickDefinitionsApplicationMode = $newValue ? self::QUICK_DEFINITIONS_MODE_MOCK_AT_LEAST_ONCE : self::QUICK_DEFINITIONS_MODE_DEFAULT_EXPECTATION; } return $this->_quickDefinitionsApplicationMode === self::QUICK_DEFINITIONS_MODE_MOCK_AT_LEAST_ONCE; } } PKGiZyA 5library/Mockery/Adapter/Phpunit/TestListenerTrait.phpnuW+AgetStatus() !== BaseTestRunner::STATUS_PASSED) { // If the test didn't pass there is no guarantee that // verifyMockObjects and assertPostConditions have been called. // And even if it did, the point here is to prevent false // negatives, not to make failing tests fail for more reasons. return; } try { // The self() call is used as a sentinel. Anything that throws if // the container is closed already will do. Mockery::self(); } catch (LogicException $logicException) { return; } $e = new ExpectationFailedException( sprintf( "Mockery's expectations have not been verified. Make sure that \Mockery::close() is called at the end of the test. Consider using %s\MockeryPHPUnitIntegration or extending %s\MockeryTestCase.", __NAMESPACE__, __NAMESPACE__ ) ); /** @var \PHPUnit\Framework\TestResult $result */ $result = $test->getTestResultObject(); if ($result !== null) { $result->addFailure($test, $e, $time); } } public function startTestSuite() { if (method_exists(Blacklist::class, 'addDirectory')) { (new Blacklist())->getBlacklistedDirectories(); Blacklist::addDirectory(dirname((new ReflectionClass(Mockery::class))->getFileName())); } else { Blacklist::$blacklistedClassNames[Mockery::class] = 1; } } } PKGiZR??=library/Mockery/Adapter/Phpunit/MockeryPHPUnitIntegration.phpnuW+AaddToAssertionCount(Mockery::getContainer()->mockery_getExpectationCount()); } protected function checkMockeryExceptions() { if (! method_exists($this, 'markAsRisky')) { return; } foreach (Mockery::getContainer()->mockery_thrownExceptions() as $e) { if (! $e->dismissed()) { $this->markAsRisky(); } } } protected function closeMockery() { Mockery::close(); $this->mockeryOpen = false; } /** * Performs assertions shared by all tests of a test case. This method is * called before execution of a test ends and before the tearDown method. */ protected function mockeryAssertPostConditions() { $this->addMockeryExpectationsToAssertionCount(); $this->checkMockeryExceptions(); $this->closeMockery(); parent::assertPostConditions(); } /** * @after */ #[After] protected function purgeMockeryContainer() { if ($this->mockeryOpen) { // post conditions wasn't called, so test probably failed Mockery::close(); } } /** * @before */ #[Before] protected function startMockery() { $this->mockeryOpen = true; } } PKGiZRR3library/Mockery/Adapter/Phpunit/MockeryTestCase.phpnuW+Atrait = new TestListenerTrait(); } public function endTest(Test $test, float $time): void { $this->trait->endTest($test, $time); } public function startTestSuite(TestSuite $suite): void { $this->trait->startTestSuite(); } } PKGiZ^ hh8library/Mockery/Adapter/Phpunit/MockeryTestCaseSetUp.phpnuW+AmockeryTestSetUp(); } protected function tearDown(): void { $this->mockeryTestTearDown(); parent::tearDown(); } } PKGiZN*  Qlibrary/Mockery/Adapter/Phpunit/MockeryPHPUnitIntegrationAssertPostConditions.phpnuW+AmockeryAssertPostConditions(); } } PKGiZӺ^ library/Mockery/Instantiator.phpnuW+A $className * * @throws InvalidArgumentException * @throws UnexpectedValueException * * @return TClass */ public function instantiate($className): object { return $this->buildFactory($className)(); } /** * @throws UnexpectedValueException */ private function attemptInstantiationViaUnSerialization( ReflectionClass $reflectionClass, string $serializedString ): void { set_error_handler(static function ($code, $message, $file, $line) use ($reflectionClass, &$error): void { $msg = sprintf( 'Could not produce an instance of "%s" via un-serialization, since an error was triggered in file "%s" at line "%d"', $reflectionClass->getName(), $file, $line ); $error = new UnexpectedValueException($msg, 0, new Exception($message, $code)); }); try { unserialize($serializedString); } catch (Exception $exception) { restore_error_handler(); throw new UnexpectedValueException( sprintf( 'An exception was raised while trying to instantiate an instance of "%s" via un-serialization', $reflectionClass->getName() ), 0, $exception ); } restore_error_handler(); if ($error instanceof UnexpectedValueException) { throw $error; } } /** * Builds a {@see Closure} capable of instantiating the given $className without invoking its constructor. */ private function buildFactory(string $className): Closure { $reflectionClass = $this->getReflectionClass($className); if ($this->isInstantiableViaReflection($reflectionClass)) { return static function () use ($reflectionClass) { return $reflectionClass->newInstanceWithoutConstructor(); }; } $serializedString = sprintf('O:%d:"%s":0:{}', strlen($className), $className); $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); return static function () use ($serializedString) { return unserialize($serializedString); }; } /** * @throws InvalidArgumentException */ private function getReflectionClass(string $className): ReflectionClass { if (! class_exists($className)) { throw new InvalidArgumentException(sprintf('Class:%s does not exist', $className)); } $reflection = new ReflectionClass($className); if ($reflection->isAbstract()) { throw new InvalidArgumentException(sprintf('Class:%s is an abstract class', $className)); } return $reflection; } /** * Verifies whether the given class is to be considered internal */ private function hasInternalAncestors(ReflectionClass $reflectionClass): bool { do { if ($reflectionClass->isInternal()) { return true; } } while ($reflectionClass = $reflectionClass->getParentClass()); return false; } /** * Verifies if the class is instantiable via reflection */ private function isInstantiableViaReflection(ReflectionClass $reflectionClass): bool { return ! ($reflectionClass->isInternal() && $reflectionClass->isFinal()); } } PKGiZ| 1f1flibrary/Mockery.phpnuW+A */ private static $_filesToCleanUp = []; /** * Return instance of AndAnyOtherArgs matcher. * * @return AndAnyOtherArgs */ public static function andAnyOtherArgs() { return new AndAnyOtherArgs(); } /** * Return instance of AndAnyOtherArgs matcher. * * An alternative name to `andAnyOtherArgs` so * the API stays closer to `any` as well. * * @return AndAnyOtherArgs */ public static function andAnyOthers() { return new AndAnyOtherArgs(); } /** * Return instance of ANY matcher. * * @return Any */ public static function any() { return new Any(); } /** * Return instance of ANYOF matcher. * * @template TAnyOf * * @param TAnyOf ...$args * * @return AnyOf */ public static function anyOf(...$args) { return new AnyOf($args); } /** * @return array * * @deprecated since 1.3.2 and will be removed in 2.0. */ public static function builtInTypes() { return ['array', 'bool', 'callable', 'float', 'int', 'iterable', 'object', 'self', 'string', 'void']; } /** * Return instance of CLOSURE matcher. * * @template TReference * * @param TReference $reference * * @return ClosureMatcher */ public static function capture(&$reference) { $closure = static function ($argument) use (&$reference) { $reference = $argument; return true; }; return new ClosureMatcher($closure); } /** * Static shortcut to closing up and verifying all mocks in the global * container, and resetting the container static variable to null. * * @return void */ public static function close() { foreach (self::$_filesToCleanUp as $fileName) { @\unlink($fileName); } self::$_filesToCleanUp = []; if (self::$_container === null) { return; } $container = self::$_container; self::$_container = null; $container->mockery_teardown(); $container->mockery_close(); } /** * Return instance of CONTAINS matcher. * * @template TContains * * @param TContains $args * * @return Contains */ public static function contains(...$args) { return new Contains($args); } /** * @param class-string $fqn * * @return void */ public static function declareClass($fqn) { static::declareType($fqn, 'class'); } /** * @param class-string $fqn * * @return void */ public static function declareInterface($fqn) { static::declareType($fqn, 'interface'); } /** * Return instance of DUCKTYPE matcher. * * @template TDucktype * * @param TDucktype ...$args * * @return Ducktype */ public static function ducktype(...$args) { return new Ducktype($args); } /** * Static fetching of a mock associated with a name or explicit class poser. * * @template TFetchMock of object * * @param class-string $name * * @return null|(LegacyMockInterface&MockInterface&TFetchMock) */ public static function fetchMock($name) { return self::getContainer()->fetchMock($name); } /** * Utility method to format method name and arguments into a string. * * @param string $method * * @return string */ public static function formatArgs($method, ?array $arguments = null) { if ($arguments === null) { return $method . '()'; } $formattedArguments = []; foreach ($arguments as $argument) { $formattedArguments[] = self::formatArgument($argument); } return $method . '(' . \implode(', ', $formattedArguments) . ')'; } /** * Utility function to format objects to printable arrays. * * @return string */ public static function formatObjects(?array $objects = null) { static $formatting; if ($formatting) { return '[Recursion]'; } if ($objects === null) { return ''; } $objects = \array_filter($objects, 'is_object'); if ($objects === []) { return ''; } $formatting = true; $parts = []; foreach ($objects as $object) { $parts[\get_class($object)] = self::objectToArray($object); } $formatting = false; return 'Objects: ( ' . \var_export($parts, true) . ')'; } /** * Lazy loader and Getter for the global * configuration container. * * @return Configuration */ public static function getConfiguration() { if (self::$_config === null) { self::$_config = new Configuration(); } return self::$_config; } /** * Lazy loader and getter for the container property. * * @return Container */ public static function getContainer() { if (self::$_container === null) { self::$_container = new Container(self::getGenerator(), self::getLoader()); } return self::$_container; } /** * Creates and returns a default generator * used inside this class. * * @return CachingGenerator */ public static function getDefaultGenerator() { return new CachingGenerator(StringManipulationGenerator::withDefaultPasses()); } /** * Gets an EvalLoader to be used as default. * * @return EvalLoader */ public static function getDefaultLoader() { return new EvalLoader(); } /** * Lazy loader method and getter for * the generator property. * * @return Generator */ public static function getGenerator() { if (self::$_generator === null) { self::$_generator = self::getDefaultGenerator(); } return self::$_generator; } /** * Lazy loader method and getter for * the $_loader property. * * @return Loader */ public static function getLoader() { if (self::$_loader === null) { self::$_loader = self::getDefaultLoader(); } return self::$_loader; } /** * Defines the global helper functions * * @return void */ public static function globalHelpers() { require_once __DIR__ . '/helpers.php'; } /** * Return instance of HASKEY matcher. * * @template THasKey * * @param THasKey $key * * @return HasKey */ public static function hasKey($key) { return new HasKey($key); } /** * Return instance of HASVALUE matcher. * * @template THasValue * * @param THasValue $val * * @return HasValue */ public static function hasValue($val) { return new HasValue($val); } /** * Static and Semantic shortcut to Container::mock(). * * @template TInstanceMock * * @param array|TInstanceMock|array> $args * * @return LegacyMockInterface&MockInterface&TInstanceMock */ public static function instanceMock(...$args) { return self::getContainer()->mock(...$args); } /** * @param string $type * * @return bool * * @deprecated since 1.3.2 and will be removed in 2.0. */ public static function isBuiltInType($type) { return \in_array($type, self::builtInTypes(), true); } /** * Return instance of IsEqual matcher. * * @template TExpected * * @param TExpected $expected */ public static function isEqual($expected): IsEqual { return new IsEqual($expected); } /** * Return instance of IsSame matcher. * * @template TExpected * * @param TExpected $expected */ public static function isSame($expected): IsSame { return new IsSame($expected); } /** * Static shortcut to Container::mock(). * * @template TMock of object * * @param array|TMock|Closure(LegacyMockInterface&MockInterface&TMock):LegacyMockInterface&MockInterface&TMock|array> $args * * @return LegacyMockInterface&MockInterface&TMock */ public static function mock(...$args) { return self::getContainer()->mock(...$args); } /** * Return instance of MUSTBE matcher. * * @template TExpected * * @param TExpected $expected * * @return MustBe */ public static function mustBe($expected) { return new MustBe($expected); } /** * Static shortcut to Container::mock(), first argument names the mock. * * @template TNamedMock * * @param array|TNamedMock|array> $args * * @return LegacyMockInterface&MockInterface&TNamedMock */ public static function namedMock(...$args) { $name = \array_shift($args); $builder = new MockConfigurationBuilder(); $builder->setName($name); \array_unshift($args, $builder); return self::getContainer()->mock(...$args); } /** * Return instance of NOT matcher. * * @template TNotExpected * * @param TNotExpected $expected * * @return Not */ public static function not($expected) { return new Not($expected); } /** * Return instance of NOTANYOF matcher. * * @template TNotAnyOf * * @param TNotAnyOf ...$args * * @return NotAnyOf */ public static function notAnyOf(...$args) { return new NotAnyOf($args); } /** * Return instance of CLOSURE matcher. * * @template TClosure of Closure * * @param TClosure $closure * * @return ClosureMatcher */ public static function on($closure) { return new ClosureMatcher($closure); } /** * Utility function to parse shouldReceive() arguments and generate * expectations from such as needed. * * @template TReturnArgs * * @param TReturnArgs ...$args * @param Closure $add * * @return CompositeExpectation */ public static function parseShouldReturnArgs(LegacyMockInterface $mock, $args, $add) { $composite = new CompositeExpectation(); foreach ($args as $arg) { if (\is_string($arg)) { $composite->add(self::buildDemeterChain($mock, $arg, $add)); continue; } if (\is_array($arg)) { foreach ($arg as $k => $v) { $composite->add(self::buildDemeterChain($mock, $k, $add)->andReturn($v)); } } } return $composite; } /** * Return instance of PATTERN matcher. * * @template TPatter * * @param TPatter $expected * * @return Pattern */ public static function pattern($expected) { return new Pattern($expected); } /** * Register a file to be deleted on tearDown. * * @param string $fileName */ public static function registerFileForCleanUp($fileName) { self::$_filesToCleanUp[] = $fileName; } /** * Reset the container to null. * * @return void */ public static function resetContainer() { self::$_container = null; } /** * Static shortcut to Container::self(). * * @throws LogicException * * @return LegacyMockInterface|MockInterface */ public static function self() { if (self::$_container === null) { throw new LogicException('You have not declared any mocks yet'); } return self::$_container->self(); } /** * Set the container. * * @return Container */ public static function setContainer(Container $container) { return self::$_container = $container; } /** * Setter for the $_generator static property. */ public static function setGenerator(Generator $generator) { self::$_generator = $generator; } /** * Setter for the $_loader static property. */ public static function setLoader(Loader $loader) { self::$_loader = $loader; } /** * Static and semantic shortcut for getting a mock from the container * and applying the spy's expected behavior into it. * * @template TSpy * * @param array|TSpy|Closure(LegacyMockInterface&MockInterface&TSpy):LegacyMockInterface&MockInterface&TSpy|array> $args * * @return LegacyMockInterface&MockInterface&TSpy */ public static function spy(...$args) { if ($args !== [] && $args[0] instanceof Closure) { $args[0] = new ClosureWrapper($args[0]); } return self::getContainer()->mock(...$args)->shouldIgnoreMissing(); } /** * Return instance of SUBSET matcher. * * @param bool $strict - (Optional) True for strict comparison, false for loose * * @return Subset */ public static function subset(array $part, $strict = true) { return new Subset($part, $strict); } /** * Return instance of TYPE matcher. * * @template TExpectedType * * @param TExpectedType $expected * * @return Type */ public static function type($expected) { return new Type($expected); } /** * Sets up expectations on the members of the CompositeExpectation and * builds up any demeter chain that was passed to shouldReceive. * * @param string $arg * @param Closure $add * * @throws MockeryException * * @return ExpectationInterface */ protected static function buildDemeterChain(LegacyMockInterface $mock, $arg, $add) { $container = $mock->mockery_getContainer(); $methodNames = \explode('->', $arg); \reset($methodNames); if ( ! $mock->mockery_isAnonymous() && ! self::getConfiguration()->mockingNonExistentMethodsAllowed() && ! \in_array(\current($methodNames), $mock->mockery_getMockableMethods(), true) ) { throw new MockeryException( "Mockery's configuration currently forbids mocking the method " . \current($methodNames) . ' as it does not exist on the class or object ' . 'being mocked' ); } /** @var Closure $nextExp */ $nextExp = static function ($method) use ($add) { return $add($method); }; $parent = \get_class($mock); /** @var null|ExpectationInterface $expectations */ $expectations = null; while (true) { $method = \array_shift($methodNames); $expectations = $mock->mockery_getExpectationsFor($method); if ($expectations === null || self::noMoreElementsInChain($methodNames)) { $expectations = $nextExp($method); if (self::noMoreElementsInChain($methodNames)) { break; } $mock = self::getNewDemeterMock($container, $parent, $method, $expectations); } else { $demeterMockKey = $container->getKeyOfDemeterMockFor($method, $parent); if ($demeterMockKey !== null) { $mock = self::getExistingDemeterMock($container, $demeterMockKey); } } $parent .= '->' . $method; $nextExp = static function ($n) use ($mock) { return $mock->allows($n); }; } return $expectations; } /** * Utility method for recursively generating a representation of the given array. * * @template TArray or array * * @param TArray $argument * @param int $nesting * * @return TArray */ private static function cleanupArray($argument, $nesting = 3) { if ($nesting === 0) { return '...'; } foreach ($argument as $key => $value) { if (\is_array($value)) { $argument[$key] = self::cleanupArray($value, $nesting - 1); continue; } if (\is_object($value)) { $argument[$key] = self::objectToArray($value, $nesting - 1); } } return $argument; } /** * Utility method used for recursively generating * an object or array representation. * * @template TArgument * * @param TArgument $argument * @param int $nesting * * @return mixed */ private static function cleanupNesting($argument, $nesting) { if (\is_object($argument)) { $object = self::objectToArray($argument, $nesting - 1); $object['class'] = \get_class($argument); return $object; } if (\is_array($argument)) { return self::cleanupArray($argument, $nesting - 1); } return $argument; } /** * @param string $fqn * @param string $type */ private static function declareType($fqn, $type): void { $targetCode = ' */ private static function extractInstancePublicProperties($object, $nesting) { $reflection = new ReflectionClass($object); $properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC); $cleanedProperties = []; foreach ($properties as $publicProperty) { if (! $publicProperty->isStatic()) { $name = $publicProperty->getName(); try { $cleanedProperties[$name] = self::cleanupNesting($object->{$name}, $nesting); } catch (Exception $exception) { $cleanedProperties[$name] = $exception->getMessage(); } } } return $cleanedProperties; } /** * Gets the string representation * of any passed argument. * * @param mixed $argument * @param int $depth * * @return mixed */ private static function formatArgument($argument, $depth = 0) { if ($argument instanceof MatcherInterface) { return (string) $argument; } if (\is_object($argument)) { return 'object(' . \get_class($argument) . ')'; } if (\is_int($argument) || \is_float($argument)) { return $argument; } if (\is_array($argument)) { if ($depth === 1) { $argument = '[...]'; } else { $sample = []; foreach ($argument as $key => $value) { $key = \is_int($key) ? $key : \sprintf("'%s'", $key); $value = self::formatArgument($value, $depth + 1); $sample[] = \sprintf('%s => %s', $key, $value); } $argument = '[' . \implode(', ', $sample) . ']'; } return (\strlen($argument) > 1000) ? \substr($argument, 0, 1000) . '...]' : $argument; } if (\is_bool($argument)) { return $argument ? 'true' : 'false'; } if (\is_resource($argument)) { return 'resource(...)'; } if ($argument === null) { return 'NULL'; } return "'" . $argument . "'"; } /** * Gets a specific demeter mock from the ones kept by the container. * * @template TMock of object * * @param class-string $demeterMockKey * * @return null|(LegacyMockInterface&MockInterface&TMock) */ private static function getExistingDemeterMock(Container $container, $demeterMockKey) { return $container->getMocks()[$demeterMockKey] ?? null; } /** * Gets a new demeter configured * mock from the container. * * @param string $parent * @param string $method * * @return LegacyMockInterface&MockInterface */ private static function getNewDemeterMock(Container $container, $parent, $method, ExpectationInterface $exp) { $newMockName = 'demeter_' . \md5($parent) . '_' . $method; $parRef = null; $parentMock = $exp->getMock(); if ($parentMock !== null) { $parRef = new ReflectionObject($parentMock); } if ($parRef instanceof ReflectionObject && $parRef->hasMethod($method)) { $parRefMethod = $parRef->getMethod($method); $parRefMethodRetType = Reflector::getReturnType($parRefMethod, true); if ($parRefMethodRetType !== null) { $returnTypes = \explode('|', $parRefMethodRetType); $filteredReturnTypes = array_filter($returnTypes, static function (string $type): bool { return ! Reflector::isReservedWord($type); }); if ($filteredReturnTypes !== []) { $nameBuilder = new MockNameBuilder(); $nameBuilder->addPart('\\' . $newMockName); $mock = self::namedMock( $nameBuilder->build(), ...$filteredReturnTypes ); $exp->andReturn($mock); return $mock; } } } $mock = $container->mock($newMockName); $exp->andReturn($mock); return $mock; } /** * Checks if the passed array representing a demeter * chain with the method names is empty. * * @return bool */ private static function noMoreElementsInChain(array $methodNames) { return $methodNames === []; } /** * Utility function to turn public properties and public get* and is* method values into an array. * * @param object $object * @param int $nesting * * @return array */ private static function objectToArray($object, $nesting = 3) { if ($nesting === 0) { return ['...']; } $defaultFormatter = static function ($object, $nesting) { return [ 'properties' => self::extractInstancePublicProperties($object, $nesting), ]; }; $class = \get_class($object); $formatter = self::getConfiguration()->getObjectFormatter($class, $defaultFormatter); $array = [ 'class' => $class, 'identity' => '#' . \md5(\spl_object_hash($object)), ]; return \array_merge($array, $formatter($object, $nesting)); } } PKGiZ,library/helpers.phpnuW+A|TMock|Closure(LegacyMockInterface&MockInterface&TMock):LegacyMockInterface&MockInterface&TMock|array> $args * * @return LegacyMockInterface&MockInterface&TMock */ function mock(...$args) { return Mockery::mock(...$args); } } if (! \function_exists('spy')) { /** * @template TSpy of object * * @param array|TSpy|Closure(LegacyMockInterface&MockInterface&TSpy):LegacyMockInterface&MockInterface&TSpy|array> $args * * @return LegacyMockInterface&MockInterface&TSpy */ function spy(...$args) { return Mockery::spy(...$args); } } if (! \function_exists('namedMock')) { /** * @template TNamedMock of object * * @param array|TNamedMock|array> $args * * @return LegacyMockInterface&MockInterface&TNamedMock */ function namedMock(...$args) { return Mockery::namedMock(...$args); } } if (! \function_exists('anyArgs')) { function anyArgs(): AnyArgs { return new AnyArgs(); } } if (! \function_exists('andAnyOtherArgs')) { function andAnyOtherArgs(): AndAnyOtherArgs { return new AndAnyOtherArgs(); } } if (! \function_exists('andAnyOthers')) { function andAnyOthers(): AndAnyOtherArgs { return new AndAnyOtherArgs(); } } PKGiZJ("" docs/conf.pynuW+A# -*- coding: utf-8 -*- # # Mockery Docs documentation build configuration file, created by # sphinx-quickstart on Mon Mar 3 14:04:26 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.todo', 'sphinx_rtd_theme', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Mockery Docs' copyright = u'Pádraic Brady, Dave Marshall and contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.6' # The full version, including alpha/beta/rc tags. release = '1.6.x' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'MockeryDocsdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'MockeryDocs.tex', u'Mockery Docs Documentation', u'Pádraic Brady, Dave Marshall, Wouter, Graham Campbell', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'mockerydocs', u'Mockery Docs Documentation', [u'Pádraic Brady, Dave Marshall, Wouter, Graham Campbell'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'MockeryDocs', u'Mockery Docs Documentation', u'Pádraic Brady, Dave Marshall, Wouter, Graham Campbell', 'MockeryDocs', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False #on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] print(sphinx_rtd_theme.get_html_theme_path()) # load PhpLexer from sphinx.highlighting import lexers from pygments.lexers.web import PhpLexer # enable highlighting for PHP code not between by default lexers['php'] = PhpLexer(startinline=True) lexers['php-annotations'] = PhpLexer(startinline=True) PKGiZdocs/_static/.gitkeepnuW+APKGiZ)-M  docs/requirements.txtnuW+Aalabaster==0.7.16 Babel==2.14.0 certifi==2024.2.2 charset-normalizer==3.3.2 docutils==0.20.1 idna==3.7 imagesize==1.4.1 Jinja2==3.1.4 MarkupSafe==2.1.5 packaging==24.0 Pygments==2.17.2 requests==2.31.0 setuptools==69.2.0 snowballstemmer==2.2.0 Sphinx==7.3.7 sphinx-rtd-theme==2.0.0 sphinxcontrib-applehelp==1.0.8 sphinxcontrib-devhelp==1.0.6 sphinxcontrib-htmlhelp==2.0.5 sphinxcontrib-jquery==4.1 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.7 sphinxcontrib-serializinghtml==1.1.10 urllib3==2.2.1 wheel==0.43.0 PKGiZJXdocs/.gitignorenuW+A_build PKGiZY~~ docs/MakefilenuW+A# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/MockeryDocs.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/MockeryDocs.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/MockeryDocs" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/MockeryDocs" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." PKGiZ;g"docs/cookbook/big_parent_class.rstnuW+A.. index:: single: Cookbook; Big Parent Class Big Parent Class ================ In some application code, especially older legacy code, we can come across some classes that extend a "big parent class" - a parent class that knows and does too much: .. code-block:: php class BigParentClass { public function doesEverything() { // sets up database connections // writes to log files } } class ChildClass extends BigParentClass { public function doesOneThing() { // but calls on BigParentClass methods $result = $this->doesEverything(); // does something with $result return $result; } } We want to test our ``ChildClass`` and its ``doesOneThing`` method, but the problem is that it calls on ``BigParentClass::doesEverything()``. One way to handle this would be to mock out **all** of the dependencies ``BigParentClass`` has and needs, and then finally actually test our ``doesOneThing`` method. It's an awful lot of work to do that. What we can do, is to do something... unconventional. We can create a runtime partial test double of the ``ChildClass`` itself and mock only the parent's ``doesEverything()`` method: .. code-block:: php $childClass = \Mockery::mock('ChildClass')->makePartial(); $childClass->shouldReceive('doesEverything') ->andReturn('some result from parent'); $childClass->doesOneThing(); // string("some result from parent"); With this approach we mock out only the ``doesEverything()`` method, and all the unmocked methods are called on the actual ``ChildClass`` instance. PKGiZIi==,docs/cookbook/mocking_class_within_class.rstnuW+A.. index:: single: Cookbook; Mocking class within class .. _mocking-class-within-class: Mocking class within class ========================== Imagine a case where you need to create an instance of a class and use it within the same method: .. code-block:: php // Point.php setPoint($x1, $y1); $b = new Point(); $b->setPoint($x2, $y1); $c = new Point(); $c->setPoint($x2, $y2); $d = new Point(); $d->setPoint($x1, $y2); $this->draw([$a, $b, $c, $d]); } public function draw($points) { echo "Do something with the points"; } } And that you want to test that a logic in ``Rectangle->create()`` calls properly each used thing - in this case calls ``Point->setPoint()``, but ``Rectangle->draw()`` does some graphical stuff that you want to avoid calling. You set the mocks for ``App\Point`` and ``App\Rectangle``: .. code-block:: php shouldReceive("setPoint")->andThrow(Exception::class); $rect = Mockery::mock("App\Rectangle")->makePartial(); $rect->shouldReceive("draw"); $rect->create(0, 0, 100, 100); // does not throw exception Mockery::close(); } } and the test does not work. Why? The mocking relies on the class not being present yet, but the class is autoloaded therefore the mock alone for ``App\Point`` is useless which you can see with ``echo`` being executed. Mocks however work for the first class in the order of loading i.e. ``App\Rectangle``, which loads the ``App\Point`` class. In more complex example that would be a single point that initiates the whole loading (``use Class``) such as:: A // main loading initiator |- B // another loading initiator | |-E | +-G | |- C // another loading initiator | +-F | +- D That basically means that the loading prevents mocking and for each such a loading initiator there needs to be implemented a workaround. Overloading is one approach, however it pollutes the global state. In this case we try to completely avoid the global state pollution with custom ``new Class()`` behavior per loading initiator and that can be mocked easily in few critical places. That being said, although we can't stop loading, we can return mocks. Let's look at ``Rectangle->create()`` method: .. code-block:: php class Rectangle { public function newPoint() { return new Point(); } public function create($x1, $y1, $x2, $y2) { $a = $this->newPoint(); $a->setPoint($x1, $y1); ... } ... } We create a custom function to encapsulate ``new`` keyword that would otherwise just use the autoloaded class ``App\Point`` and in our test we mock that function so that it returns our mock: .. code-block:: php shouldReceive("setPoint")->andThrow(Exception::class); $rect = Mockery::mock("App\Rectangle")->makePartial(); $rect->shouldReceive("draw"); // pass the App\Point mock into App\Rectangle as an alternative // to using new App\Point() in-place. $rect->shouldReceive("newPoint")->andReturn($point); $this->expectException(Exception::class); $rect->create(0, 0, 100, 100); Mockery::close(); } } If we run this test now, it should pass. For more complex cases we'd find the next loader in the program flow and proceed with wrapping and passing mock instances with predefined behavior into already existing classes. PKGiZ:docs/cookbook/map.rst.incnuW+A* :doc:`/cookbook/default_expectations` * :doc:`/cookbook/detecting_mock_objects` * :doc:`/cookbook/not_calling_the_constructor` * :doc:`/cookbook/mocking_hard_dependencies` * :doc:`/cookbook/class_constants` * :doc:`/cookbook/big_parent_class` * :doc:`/cookbook/mockery_on` PKGiZ=b(docs/cookbook/detecting_mock_objects.rstnuW+A.. index:: single: Cookbook; Detecting Mock Objects Detecting Mock Objects ====================== Users may find it useful to check whether a given object is a real object or a simulated Mock Object. All Mockery mocks implement the ``\Mockery\MockInterface`` interface which can be used in a type check. .. code-block:: php assert($mightBeMocked instanceof \Mockery\MockInterface); PKGiZrxx+docs/cookbook/mocking_hard_dependencies.rstnuW+A.. index:: single: Cookbook; Mocking Hard Dependencies Mocking Hard Dependencies (new Keyword) ======================================= One prerequisite to mock hard dependencies is that the code we are trying to test uses autoloading. Let's take the following code for an example: .. code-block:: php sendSomething($param); return $externalService->getSomething(); } } The way we can test this without doing any changes to the code itself is by creating :doc:`instance mocks ` by using the ``overload`` prefix. .. code-block:: php shouldReceive('sendSomething') ->once() ->with($param); $externalMock->shouldReceive('getSomething') ->once() ->andReturn('Tested!'); $service = new \App\Service(); $result = $service->callExternalService($param); $this->assertSame('Tested!', $result); } } If we run this test now, it should pass. Mockery does its job and our ``App\Service`` will use the mocked external service instead of the real one. The problem with this is when we want to, for example, test the ``App\Service\External`` itself, or if we use that class somewhere else in our tests. When Mockery overloads a class, because of how PHP works with files, that overloaded class file must not be included otherwise Mockery will throw a "class already exists" exception. This is where autoloading kicks in and makes our job a lot easier. To make this possible, we'll tell PHPUnit to run the tests that have overloaded classes in separate processes and to not preserve global state. That way we'll avoid having the overloaded class included more than once. Of course this has its downsides as these tests will run slower. Our test example from above now becomes: .. code-block:: php shouldReceive('sendSomething') ->once() ->with($param); $externalMock->shouldReceive('getSomething') ->once() ->andReturn('Tested!'); $service = new \App\Service(); $result = $service->callExternalService($param); $this->assertSame('Tested!', $result); } } Testing the constructor arguments of hard Dependencies ------------------------------------------------------ Sometimes we might want to ensure that the hard dependency is instantiated with particular arguments. With overloaded mocks, we can set up expectations on the constructor. .. code-block:: php allows('sendSomething'); $externalMock->shouldReceive('__construct') ->once() ->with(5); $service = new \App\Service(); $result = $service->callExternalService($param); } } .. note:: For more straightforward and single-process tests oriented way check :ref:`mocking-class-within-class`. .. note:: This cookbook entry is an adaption of the blog post titled `"Mocking hard dependencies with Mockery" `_, published by Robert Basic on his blog. PKGiZ?8E docs/cookbook/mockery_on.rstnuW+A.. index:: single: Cookbook; Complex Argument Matching With Mockery::on Complex Argument Matching With Mockery::on ========================================== When we need to do a more complex argument matching for an expected method call, the ``\Mockery::on()`` matcher comes in really handy. It accepts a closure as an argument and that closure in turn receives the argument passed in to the method, when called. If the closure returns ``true``, Mockery will consider that the argument has passed the expectation. If the closure returns ``false``, or a "falsey" value, the expectation will not pass. The ``\Mockery::on()`` matcher can be used in various scenarios — validating an array argument based on multiple keys and values, complex string matching... Say, for example, we have the following code. It doesn't do much; publishes a post by setting the ``published`` flag in the database to ``1`` and sets the ``published_at`` to the current date and time: .. code-block:: php model = $model; } public function publishPost($id) { $saveData = [ 'post_id' => $id, 'published' => 1, 'published_at' => gmdate('Y-m-d H:i:s'), ]; $this->model->save($saveData); } } In a test we would mock the model and set some expectations on the call of the ``save()`` method: .. code-block:: php shouldReceive('save') ->once() ->with(\Mockery::on(function ($argument) use ($postId) { $postIdIsSet = isset($argument['post_id']) && $argument['post_id'] === $postId; $publishedFlagIsSet = isset($argument['published']) && $argument['published'] === 1; $publishedAtIsSet = isset($argument['published_at']); return $postIdIsSet && $publishedFlagIsSet && $publishedAtIsSet; })); $service = new \Service\Post($modelMock); $service->publishPost($postId); \Mockery::close(); The important part of the example is inside the closure we pass to the ``\Mockery::on()`` matcher. The ``$argument`` is actually the ``$saveData`` argument the ``save()`` method gets when it is called. We check for a couple of things in this argument: * the post ID is set, and is same as the post ID we passed in to the ``publishPost()`` method, * the ``published`` flag is set, and is ``1``, and * the ``published_at`` key is present. If any of these requirements is not satisfied, the closure will return ``false``, the method call expectation will not be met, and Mockery will throw a ``NoMatchingExpectationException``. .. note:: This cookbook entry is an adaption of the blog post titled `"Complex argument matching in Mockery" `_, published by Robert Basic on his blog. PKGiZIwdocs/cookbook/index.rstnuW+ACookbook ======== .. toctree:: :hidden: default_expectations detecting_mock_objects not_calling_the_constructor mocking_hard_dependencies class_constants big_parent_class mockery_on mocking_class_within_class .. include:: map.rst.inc PKGiZ2hh-docs/cookbook/not_calling_the_constructor.rstnuW+A.. index:: single: Cookbook; Not Calling the Original Constructor Not Calling the Original Constructor ==================================== When creating generated partial test doubles, Mockery mocks out only the method which we specifically told it to. This means that the original constructor of the class we are mocking will be called. In some cases this is not a desired behavior, as the constructor might issue calls to other methods, or other object collaborators, and as such, can create undesired side-effects in the application's environment when running the tests. If this happens, we need to use runtime partial test doubles, as they don't call the original constructor. .. code-block:: php class MyClass { public function __construct() { echo "Original constructor called." . PHP_EOL; // Other side-effects can happen... } } // This will print "Original constructor called." $mock = \Mockery::mock('MyClass[foo]'); A better approach is to use runtime partial doubles: .. code-block:: php class MyClass { public function __construct() { echo "Original constructor called." . PHP_EOL; // Other side-effects can happen... } } // This will not print anything $mock = \Mockery::mock('MyClass')->makePartial(); $mock->shouldReceive('foo'); This is one of the reason why we don't recommend using generated partial test doubles, but if possible, always use the runtime partials. Read more about :ref:`creating-test-doubles-partial-test-doubles`. .. note:: The way generated partial test doubles work, is a BC break. If you use a really old version of Mockery, it might behave in a way that the constructor is not being called for these generated partials. In the case if you upgrade to a more recent version of Mockery, you'll probably have to change your tests to use runtime partials, instead of generated ones. This change was introduced in early 2013, so it is highly unlikely that you are using a Mockery from before that, so this should not be an issue. PKGiZ$wF&docs/cookbook/default_expectations.rstnuW+A.. index:: single: Cookbook; Default Mock Expectations Default Mock Expectations ========================= Often in unit testing, we end up with sets of tests which use the same object dependency over and over again. Rather than mocking this class/object within every single unit test (requiring a mountain of duplicate code), we can instead define reusable default mocks within the test case's ``setup()`` method. This even works where unit tests use varying expectations on the same or similar mock object. How this works, is that you can define mocks with default expectations. Then, in a later unit test, you can add or fine-tune expectations for that specific test. Any expectation can be set as a default using the ``byDefault()`` declaration. PKGiZ$DD!docs/cookbook/class_constants.rstnuW+A.. index:: single: Cookbook; Class Constants Class Constants =============== When creating a test double for a class, Mockery does not create stubs out of any class constants defined in the class we are mocking. Sometimes though, the non-existence of these class constants, setup of the test, and the application code itself, it can lead to undesired behavior, and even a PHP error: ``PHP Fatal error: Uncaught Error: Undefined class constant 'FOO' in ...`` While supporting class constants in Mockery would be possible, it does require an awful lot of work, for a small number of use cases. Named Mocks ----------- We can, however, deal with these constants in a way supported by Mockery - by using :ref:`creating-test-doubles-named-mocks`. A named mock is a test double that has a name of the class we want to mock, but under it is a stubbed out class that mimics the real class with canned responses. Lets look at the following made up, but not impossible scenario: .. code-block:: php class Fetcher { const SUCCESS = 0; const FAILURE = 1; public static function fetch() { // Fetcher gets something for us from somewhere... return self::SUCCESS; } } class MyClass { public function doFetching() { $response = Fetcher::fetch(); if ($response == Fetcher::SUCCESS) { echo "Thanks!" . PHP_EOL; } else { echo "Try again!" . PHP_EOL; } } } Our ``MyClass`` calls a ``Fetcher`` that fetches some resource from somewhere - maybe it downloads a file from a remote web service. Our ``MyClass`` prints out a response message depending on the response from the ``Fetcher::fetch()`` call. When testing ``MyClass`` we don't really want ``Fetcher`` to go and download random stuff from the internet every time we run our test suite. So we mock it out: .. code-block:: php // Using alias: because fetch is called statically! \Mockery::mock('alias:Fetcher') ->shouldReceive('fetch') ->andReturn(0); $myClass = new MyClass(); $myClass->doFetching(); If we run this, our test will error out with a nasty ``PHP Fatal error: Uncaught Error: Undefined class constant 'SUCCESS' in ..``. Here's how a ``namedMock()`` can help us in a situation like this. We create a stub for the ``Fetcher`` class, stubbing out the class constants, and then use ``namedMock()`` to create a mock named ``Fetcher`` based on our stub: .. code-block:: php class FetcherStub { const SUCCESS = 0; const FAILURE = 1; } \Mockery::namedMock('Fetcher', 'FetcherStub') ->shouldReceive('fetch') ->andReturn(0); $myClass = new MyClass(); $myClass->doFetching(); This works because under the hood, Mockery creates a class called ``Fetcher`` that extends ``FetcherStub``. The same approach will work even if ``Fetcher::fetch()`` is not a static dependency: .. code-block:: php class Fetcher { const SUCCESS = 0; const FAILURE = 1; public function fetch() { // Fetcher gets something for us from somewhere... return self::SUCCESS; } } class MyClass { public function doFetching($fetcher) { $response = $fetcher->fetch(); if ($response == Fetcher::SUCCESS) { echo "Thanks!" . PHP_EOL; } else { echo "Try again!" . PHP_EOL; } } } And the test will have something like this: .. code-block:: php class FetcherStub { const SUCCESS = 0; const FAILURE = 1; } $mock = \Mockery::mock('Fetcher', 'FetcherStub') $mock->shouldReceive('fetch') ->andReturn(0); $myClass = new MyClass(); $myClass->doFetching($mock); Constants Map ------------- Another way of mocking class constants can be with the use of the constants map configuration. Given a class with constants: .. code-block:: php class Fetcher { const SUCCESS = 0; const FAILURE = 1; public function fetch() { // Fetcher gets something for us from somewhere... return self::SUCCESS; } } It can be mocked with: .. code-block:: php \Mockery::getConfiguration()->setConstantsMap([ 'Fetcher' => [ 'SUCCESS' => 'success', 'FAILURE' => 'fail', ] ]); $mock = \Mockery::mock('Fetcher'); var_dump($mock::SUCCESS); // (string) 'success' var_dump($mock::FAILURE); // (string) 'fail' PKGiZ8n"Y+docs/reference/public_static_properties.rstnuW+A.. index:: single: Mocking; Public Static Methods Mocking Public Static Methods ============================= Static methods are not called on real objects, so normal mock objects can't mock them. Mockery supports class aliased mocks, mocks representing a class name which would normally be loaded (via autoloading or a require statement) in the system under test. These aliases block that loading (unless via a require statement - so please use autoloading!) and allow Mockery to intercept static method calls and add expectations for them. See the :ref:`creating-test-doubles-aliasing` section for more information on creating aliased mocks, for the purpose of mocking public static methods. PKGiZעVM M 4docs/reference/alternative_should_receive_syntax.rstnuW+A.. index:: single: Alternative shouldReceive Syntax Alternative shouldReceive Syntax ================================ As of Mockery 1.0.0, we support calling methods as we would call any PHP method, and not as string arguments to Mockery ``should*`` methods. The two Mockery methods that enable this are ``allows()`` and ``expects()``. Allows ------ We use ``allows()`` when we create stubs for methods that return a predefined return value, but for these method stubs we don't care how many times, or if at all, were they called. .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->allows([ 'name_of_method_1' => 'return value', 'name_of_method_2' => 'return value', ]); This is equivalent with the following ``shouldReceive`` syntax: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive([ 'name_of_method_1' => 'return value', 'name_of_method_2' => 'return value', ]); Note that with this format, we also tell Mockery that we don't care about the arguments to the stubbed methods. If we do care about the arguments, we would do it like so: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->allows() ->name_of_method_1($arg1) ->andReturn('return value'); This is equivalent with the following ``shouldReceive`` syntax: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method_1') ->with($arg1) ->andReturn('return value'); Expects ------- We use ``expects()`` when we want to verify that a particular method was called: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->expects() ->name_of_method_1($arg1) ->andReturn('return value'); This is equivalent with the following ``shouldReceive`` syntax: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method_1') ->once() ->with($arg1) ->andReturn('return value'); By default ``expects()`` sets up an expectation that the method should be called once and once only. If we expect more than one call to the method, we can change that expectation: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->expects() ->name_of_method_1($arg1) ->twice() ->andReturn('return value'); PKGiZ4~8~8(docs/reference/creating_test_doubles.rstnuW+A.. index:: single: Reference; Creating Test Doubles Creating Test Doubles ===================== Mockery's main goal is to help us create test doubles. It can create stubs, mocks, and spies. Stubs and mocks are created the same. The difference between the two is that a stub only returns a preset result when called, while a mock needs to have expectations set on the method calls it expects to receive. Spies are a type of test doubles that keep track of the calls they received, and allow us to inspect these calls after the fact. When creating a test double object, we can pass in an identifier as a name for our test double. If we pass it no identifier, the test double name will be unknown. Furthermore, the identifier does not have to be a class name. It is a good practice, and our recommendation, to always name the test doubles with the same name as the underlying class we are creating test doubles for. If the identifier we use for our test double is a name of an existing class, the test double will inherit the type of the class (via inheritance), i.e. the mock object will pass type hints or ``instanceof`` evaluations for the existing class. This is useful when a test double must be of a specific type, to satisfy the expectations our code has. Stubs and mocks --------------- Stubs and mocks are created by calling the ``\Mockery::mock()`` method. The following example shows how to create a stub, or a mock, object named "foo": .. code-block:: php $mock = \Mockery::mock('foo'); The mock object created like this is the loosest form of mocks possible, and is an instance of ``\Mockery\MockInterface``. .. note:: All test doubles created with Mockery are an instance of ``\Mockery\MockInterface``, regardless are they a stub, mock or a spy. To create a stub or a mock object with no name, we can call the ``mock()`` method with no parameters: .. code-block:: php $mock = \Mockery::mock(); As we stated earlier, we don't recommend creating stub or mock objects without a name. Classes, abstracts, interfaces ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The recommended way to create a stub or a mock object is by using a name of an existing class we want to create a test double of: .. code-block:: php $mock = \Mockery::mock('MyClass'); This stub or mock object will have the type of ``MyClass``, through inheritance. Stub or mock objects can be based on any concrete class, abstract class or even an interface. The primary purpose is to ensure the mock object inherits a specific type for type hinting. .. code-block:: php $mock = \Mockery::mock('MyInterface'); This stub or mock object will implement the ``MyInterface`` interface. .. note:: Classes marked final, or classes that have methods marked final cannot be mocked fully. Mockery supports creating partial mocks for these cases. Partial mocks will be explained later in the documentation. Mockery also supports creating stub or mock objects based on a single existing class, which must implement one or more interfaces. We can do this by providing a comma-separated list of the class and interfaces as the first argument to the ``\Mockery::mock()`` method: .. code-block:: php $mock = \Mockery::mock('MyClass, MyInterface, OtherInterface'); This stub or mock object will now be of type ``MyClass`` and implement the ``MyInterface`` and ``OtherInterface`` interfaces. .. note:: The class name doesn't need to be the first member of the list but it's a friendly convention to use for readability. We can tell a mock to implement the desired interfaces by passing the list of interfaces as the second argument: .. code-block:: php $mock = \Mockery::mock('MyClass', 'MyInterface, OtherInterface'); For all intents and purposes, this is the same as the previous example. Spies ----- The third type of test doubles Mockery supports are spies. The main difference between spies and mock objects is that with spies we verify the calls made against our test double after the calls were made. We would use a spy when we don't necessarily care about all of the calls that are going to be made to an object. A spy will return ``null`` for all method calls it receives. It is not possible to tell a spy what will be the return value of a method call. If we do that, then we would deal with a mock object, and not with a spy. We create a spy by calling the ``\Mockery::spy()`` method: .. code-block:: php $spy = \Mockery::spy('MyClass'); Just as with stubs or mocks, we can tell Mockery to base a spy on any concrete or abstract class, or to implement any number of interfaces: .. code-block:: php $spy = \Mockery::spy('MyClass, MyInterface, OtherInterface'); This spy will now be of type ``MyClass`` and implement the ``MyInterface`` and ``OtherInterface`` interfaces. .. note:: The ``\Mockery::spy()`` method call is actually a shorthand for calling ``\Mockery::mock()->shouldIgnoreMissing()``. The ``shouldIgnoreMissing`` method is a "behaviour modifier". We'll discuss them a bit later. Mocks vs. Spies --------------- Let's try and illustrate the difference between mocks and spies with the following example: .. code-block:: php $mock = \Mockery::mock('MyClass'); $spy = \Mockery::spy('MyClass'); $mock->shouldReceive('foo')->andReturn(42); $mockResult = $mock->foo(); $spyResult = $spy->foo(); $spy->shouldHaveReceived()->foo(); var_dump($mockResult); // int(42) var_dump($spyResult); // null As we can see from this example, with a mock object we set the call expectations before the call itself, and we get the return result we expect it to return. With a spy object on the other hand, we verify the call has happened after the fact. The return result of a method call against a spy is always ``null``. We also have a dedicated chapter to :doc:`spies` only. .. _creating-test-doubles-partial-test-doubles: Partial Test Doubles -------------------- Partial doubles are useful when we want to stub out, set expectations for, or spy on *some* methods of a class, but run the actual code for other methods. We differentiate between three types of partial test doubles: * runtime partial test doubles, * generated partial test doubles, and * proxied partial test doubles. Runtime partial test doubles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ What we call a runtime partial, involves creating a test double and then telling it to make itself partial. Any method calls that the double hasn't been told to allow or expect, will act as they would on a normal instance of the object. .. code-block:: php class Foo { function foo() { return 123; } function bar() { return $this->foo(); } } $foo = mock(Foo::class)->makePartial(); $foo->foo(); // int(123); We can then tell the test double to allow or expect calls as with any other Mockery double. .. code-block:: php $foo->shouldReceive('foo')->andReturn(456); $foo->bar(); // int(456) See the cookbook entry on :doc:`../cookbook/big_parent_class` for an example usage of runtime partial test doubles. Generated partial test doubles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The second type of partial double we can create is what we call a generated partial. With generated partials, we specifically tell Mockery which methods we want to be able to allow or expect calls to. All other methods will run the actual code *directly*, so stubs and expectations on these methods will not work. .. code-block:: php class Foo { function foo() { return 123; } function bar() { return $this->foo(); } } $foo = mock("Foo[foo]"); $foo->foo(); // error, no expectation set $foo->shouldReceive('foo')->andReturn(456); $foo->foo(); // int(456) // setting an expectation for this has no effect $foo->shouldReceive('bar')->andReturn(999); $foo->bar(); // int(456) It's also possible to specify explicitly which methods to run directly using the `!method` syntax: .. code-block:: php class Foo { function foo() { return 123; } function bar() { return $this->foo(); } } $foo = mock("Foo[!foo]"); $foo->foo(); // int(123) $foo->bar(); // error, no expectation set .. note:: Even though we support generated partial test doubles, we do not recommend using them. One of the reasons why is because a generated partial will call the original constructor of the mocked class. This can have unwanted side-effects during testing application code. See :doc:`../cookbook/not_calling_the_constructor` for more details. Proxied partial test doubles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A proxied partial mock is a partial of last resort. We may encounter a class which is simply not capable of being mocked because it has been marked as final. Similarly, we may find a class with methods marked as final. In such a scenario, we cannot simply extend the class and override methods to mock - we need to get creative. .. code-block:: php $mock = \Mockery::mock(new MyClass); Yes, the new mock is a Proxy. It intercepts calls and reroutes them to the proxied object (which we construct and pass in) for methods which are not subject to any expectations. Indirectly, this allows us to mock methods marked final since the Proxy is not subject to those limitations. The tradeoff should be obvious - a proxied partial will fail any typehint checks for the class being mocked since it cannot extend that class. .. _creating-test-doubles-aliasing: Aliasing -------- Prefixing the valid name of a class (which is NOT currently loaded) with "alias:" will generate an "alias mock". Alias mocks create a class alias with the given classname to stdClass and are generally used to enable the mocking of public static methods. Expectations set on the new mock object which refer to static methods will be used by all static calls to this class. .. code-block:: php $mock = \Mockery::mock('alias:MyClass'); .. note:: Even though aliasing classes is supported, we do not recommend it. Overloading ----------- Prefixing the valid name of a class (which is NOT currently loaded) with "overload:" will generate an alias mock (as with "alias:") except that created new instances of that class will import any expectations set on the origin mock (``$mock``). The origin mock is never verified since it's used an expectation store for new instances. For this purpose we use the term "instance mock" to differentiate it from the simpler "alias mock". In other words, an instance mock will "intercept" when a new instance of the mocked class is created, then the mock will be used instead. This is useful especially when mocking hard dependencies which will be discussed later. .. code-block:: php $mock = \Mockery::mock('overload:MyClass'); .. note:: Using alias/instance mocks across more than one test will generate a fatal error since we can't have two classes of the same name. To avoid this, run each test of this kind in a separate PHP process (which is supported out of the box by both PHPUnit and PHPT). .. _creating-test-doubles-named-mocks: Named Mocks ----------- The ``namedMock()`` method will generate a class called by the first argument, so in this example ``MyClassName``. The rest of the arguments are treated in the same way as the ``mock`` method: .. code-block:: php $mock = \Mockery::namedMock('MyClassName', 'DateTime'); This example would create a class called ``MyClassName`` that extends ``DateTime``. Named mocks are quite an edge case, but they can be useful when code depends on the ``__CLASS__`` magic constant, or when we need two derivatives of an abstract type, that are actually different classes. See the cookbook entry on :doc:`../cookbook/class_constants` for an example usage of named mocks. .. note:: We can only create a named mock once, any subsequent calls to ``namedMock``, with different arguments are likely to cause exceptions. .. _creating-test-doubles-constructor-arguments: Constructor Arguments --------------------- Sometimes the mocked class has required constructor arguments. We can pass these to Mockery as an indexed array, as the 2nd argument: .. code-block:: php $mock = \Mockery::mock('MyClass', [$constructorArg1, $constructorArg2]); or if we need the ``MyClass`` to implement an interface as well, as the 3rd argument: .. code-block:: php $mock = \Mockery::mock('MyClass', 'MyInterface', [$constructorArg1, $constructorArg2]); Mockery now knows to pass in ``$constructorArg1`` and ``$constructorArg2`` as arguments to the constructor. .. _creating-test-doubles-behavior-modifiers: Behavior Modifiers ------------------ When creating a mock object, we may wish to use some commonly preferred behaviours that are not the default in Mockery. The use of the ``shouldIgnoreMissing()`` behaviour modifier will label this mock object as a Passive Mock: .. code-block:: php \Mockery::mock('MyClass')->shouldIgnoreMissing(); In such a mock object, calls to methods which are not covered by expectations will return ``null`` instead of the usual error about there being no expectation matching the call. On PHP >= 7.0.0, methods with missing expectations that have a return type will return either a mock of the object (if return type is a class) or a "falsy" primitive value, e.g. empty string, empty array, zero for ints and floats, false for bools, or empty closures. On PHP >= 7.1.0, methods with missing expectations and nullable return type will return null. We can optionally prefer to return an object of type ``\Mockery\Undefined`` (i.e. a ``null`` object) (which was the 0.7.2 behaviour) by using an additional modifier: .. code-block:: php \Mockery::mock('MyClass')->shouldIgnoreMissing()->asUndefined(); The returned object is nothing more than a placeholder so if, by some act of fate, it's erroneously used somewhere it shouldn't, it will likely not pass a logic check. We have encountered the ``makePartial()`` method before, as it is the method we use to create runtime partial test doubles: .. code-block:: php \Mockery::mock('MyClass')->makePartial(); This form of mock object will defer all methods not subject to an expectation to the parent class of the mock, i.e. ``MyClass``. Whereas the previous ``shouldIgnoreMissing()`` returned ``null``, this behaviour simply calls the parent's matching method. PKGiZa%%#docs/reference/instance_mocking.rstnuW+A.. index:: single: Mocking; Instance Instance Mocking ================ Instance mocking means that a statement like: .. code-block:: php $obj = new \MyNamespace\Foo; ...will actually generate a mock object. This is done by replacing the real class with an instance mock (similar to an alias mock), as with mocking public methods. The alias will import its expectations from the original mock of that type (note that the original is never verified and should be ignored after its expectations are setup). This lets you intercept instantiation where you can't simply inject a replacement object. As before, this does not prevent a require statement from including the real class and triggering a fatal PHP error. It's intended for use where autoloading is the primary class loading mechanism. PKGiZ2>&&docs/reference/map.rst.incnuW+A* :doc:`/reference/creating_test_doubles` * :doc:`/reference/expectations` * :doc:`/reference/argument_validation` * :doc:`/reference/alternative_should_receive_syntax` * :doc:`/reference/spies` * :doc:`/reference/partial_mocks` * :doc:`/reference/protected_methods` * :doc:`/reference/public_properties` * :doc:`/reference/public_static_properties` * :doc:`/reference/pass_by_reference_behaviours` * :doc:`/reference/demeter_chains` * :doc:`/reference/final_methods_classes` * :doc:`/reference/magic_methods` * :doc:`/reference/phpunit_integration` PKGiZA. &docs/reference/phpunit_integration.rstnuW+A.. index:: single: PHPUnit Integration PHPUnit Integration =================== Mockery was designed as a simple-to-use *standalone* mock object framework, so its need for integration with any testing framework is entirely optional. To integrate Mockery, we need to define a ``tearDown()`` method for our tests containing the following (we may use a shorter ``\Mockery`` namespace alias): .. code-block:: php public function tearDown() { \Mockery::close(); } This static call cleans up the Mockery container used by the current test, and run any verification tasks needed for our expectations. For some added brevity when it comes to using Mockery, we can also explicitly use the Mockery namespace with a shorter alias. For example: .. code-block:: php use \Mockery as m; class SimpleTest extends \PHPUnit\Framework\TestCase { public function testSimpleMock() { $mock = m::mock('simplemock'); $mock->shouldReceive('foo')->with(5, m::any())->once()->andReturn(10); $this->assertEquals(10, $mock->foo(5)); } public function tearDown() { m::close(); } } Mockery ships with an autoloader so we don't need to litter our tests with ``require_once()`` calls. To use it, ensure Mockery is on our ``include_path`` and add the following to our test suite's ``Bootstrap.php`` or ``TestHelper.php`` file: .. code-block:: php require_once 'Mockery/Loader.php'; require_once 'Hamcrest/Hamcrest.php'; $loader = new \Mockery\Loader; $loader->register(); If we are using Composer, we can simplify this to including the Composer generated autoloader file: .. code-block:: php require __DIR__ . '/../vendor/autoload.php'; // assuming vendor is one directory up .. caution:: Prior to Hamcrest 1.0.0, the ``Hamcrest.php`` file name had a small "h" (i.e. ``hamcrest.php``). If upgrading Hamcrest to 1.0.0 remember to check the file name is updated for all your projects.) To integrate Mockery into PHPUnit and avoid having to call the close method and have Mockery remove itself from code coverage reports, have your test case extends the ``\Mockery\Adapter\Phpunit\MockeryTestCase``: .. code-block:: php class MyTest extends \Mockery\Adapter\Phpunit\MockeryTestCase { } An alternative is to use the supplied trait: .. code-block:: php class MyTest extends \PHPUnit\Framework\TestCase { use \Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; } Extending ``MockeryTestCase`` or using the ``MockeryPHPUnitIntegration`` trait is **the recommended way** of integrating Mockery with PHPUnit, since Mockery 1.0.0. PHPUnit listener ---------------- Before the 1.0.0 release, Mockery provided a PHPUnit listener that would call ``Mockery::close()`` for us at the end of a test. This has changed significantly since the 1.0.0 version. Now, Mockery provides a PHPUnit listener that makes tests fail if ``Mockery::close()`` has not been called. It can help identify tests where we've forgotten to include the trait or extend the ``MockeryTestCase``. If we are using PHPUnit's XML configuration approach, we can include the following to load the ``TestListener``: .. code-block:: xml Make sure Composer's or Mockery's autoloader is present in the bootstrap file or we will need to also define a "file" attribute pointing to the file of the ``TestListener`` class. If we are creating the test suite programmatically we may add the listener like this: .. code-block:: php // Create the suite. $suite = new PHPUnit\Framework\TestSuite(); // Create the listener and add it to the suite. $result = new PHPUnit\Framework\TestResult(); $result->addListener(new \Mockery\Adapter\Phpunit\TestListener()); // Run the tests. $suite->run($result); .. caution:: PHPUnit provides a functionality that allows `tests to run in a separated process `_, to ensure better isolation. Mockery verifies the mocks expectations using the ``Mockery::close()`` method, and provides a PHPUnit listener, that automatically calls this method for us after every test. However, this listener is not called in the right process when using PHPUnit's process isolation, resulting in expectations that might not be respected, but without raising any ``Mockery\Exception``. To avoid this, we cannot rely on the supplied Mockery PHPUnit ``TestListener``, and we need to explicitly call ``Mockery::close``. The easiest solution to include this call in the ``tearDown()`` method, as explained previously. PKGiZbgg!docs/reference/demeter_chains.rstnuW+A.. index:: single: Mocking; Demeter Chains Mocking Demeter Chains And Fluent Interfaces ============================================ Both of these terms refer to the growing practice of invoking statements similar to: .. code-block:: php $object->foo()->bar()->zebra()->alpha()->selfDestruct(); The long chain of method calls isn't necessarily a bad thing, assuming they each link back to a local object the calling class knows. As a fun example, Mockery's long chains (after the first ``shouldReceive()`` method) all call to the same instance of ``\Mockery\Expectation``. However, sometimes this is not the case and the chain is constantly crossing object boundaries. In either case, mocking such a chain can be a horrible task. To make it easier Mockery supports demeter chain mocking. Essentially, we shortcut through the chain and return a defined value from the final call. For example, let's assume ``selfDestruct()`` returns the string "Ten!" to $object (an instance of ``CaptainsConsole``). Here's how we could mock it. .. code-block:: php $mock = \Mockery::mock('CaptainsConsole'); $mock->shouldReceive('foo->bar->zebra->alpha->selfDestruct')->andReturn('Ten!'); The above expectation can follow any previously seen format or expectation, except that the method name is simply the string of all expected chain calls separated by ``->``. Mockery will automatically setup the chain of expected calls with its final return values, regardless of whatever intermediary object might be used in the real implementation. Arguments to all members of the chain (except the final call) are ignored in this process. PKGiZ437 docs/reference/partial_mocks.rstnuW+A.. index:: single: Mocking; Partial Mocks Creating Partial Mocks ====================== Partial mocks are useful when we only need to mock several methods of an object leaving the remainder free to respond to calls normally (i.e. as implemented). Mockery implements three distinct strategies for creating partials. Each has specific advantages and disadvantages so which strategy we use will depend on our own preferences and the source code in need of mocking. We have previously talked a bit about :ref:`creating-test-doubles-partial-test-doubles`, but we'd like to expand on the subject a bit here. #. Runtime partial test doubles #. Generated partial test doubles #. Proxied Partial Mock Runtime partial test doubles ---------------------------- A runtime partial test double, also known as a passive partial mock, is a kind of a default state of being for a mocked object. .. code-block:: php $mock = \Mockery::mock('MyClass')->makePartial(); With a runtime partial, we assume that all methods will simply defer to the parent class (``MyClass``) original methods unless a method call matches a known expectation. If we have no matching expectation for a specific method call, that call is deferred to the class being mocked. Since the division between mocked and unmocked calls depends entirely on the expectations we define, there is no need to define which methods to mock in advance. See the cookbook entry on :doc:`../cookbook/big_parent_class` for an example usage of runtime partial test doubles. Generated Partial Test Doubles ------------------------------ A generated partial test double, also known as a traditional partial mock, defines ahead of time which methods of a class are to be mocked and which are to be left unmocked (i.e. callable as normal). The syntax for creating traditional mocks is: .. code-block:: php $mock = \Mockery::mock('MyClass[foo,bar]'); In the above example, the ``foo()`` and ``bar()`` methods of MyClass will be mocked but no other MyClass methods are touched. We will need to define expectations for the ``foo()`` and ``bar()`` methods to dictate their mocked behaviour. Don't forget that we can pass in constructor arguments since unmocked methods may rely on those! .. code-block:: php $mock = \Mockery::mock('MyNamespace\MyClass[foo]', array($arg1, $arg2)); See the :ref:`creating-test-doubles-constructor-arguments` section to read up on them. .. note:: Even though we support generated partial test doubles, we do not recommend using them. Proxied Partial Mock -------------------- A proxied partial mock is a partial of last resort. We may encounter a class which is simply not capable of being mocked because it has been marked as final. Similarly, we may find a class with methods marked as final. In such a scenario, we cannot simply extend the class and override methods to mock - we need to get creative. .. code-block:: php $mock = \Mockery::mock(new MyClass); Yes, the new mock is a Proxy. It intercepts calls and reroutes them to the proxied object (which we construct and pass in) for methods which are not subject to any expectations. Indirectly, this allows us to mock methods marked final since the Proxy is not subject to those limitations. The tradeoff should be obvious - a proxied partial will fail any typehint checks for the class being mocked since it cannot extend that class. Special Internal Cases ---------------------- All mock objects, with the exception of Proxied Partials, allows us to make any expectation call to the underlying real class method using the ``passthru()`` expectation call. This will return values from the real call and bypass any mocked return queue (which can simply be omitted obviously). There is a fourth kind of partial mock reserved for internal use. This is automatically generated when we attempt to mock a class containing methods marked final. Since we cannot override such methods, they are simply left unmocked. Typically, we don't need to worry about this but if we really really must mock a final method, the only possible means is through a Proxied Partial Mock. SplFileInfo, for example, is a common class subject to this form of automatic internal partial since it contains public final methods used internally. PKGiZQ55$docs/reference/public_properties.rstnuW+A.. index:: single: Mocking; Public Properties Mocking Public Properties ========================= Mockery allows us to mock properties in several ways. One way is that we can set a public property and its value on any mock object. The second is that we can use the expectation methods ``set()`` and ``andSet()`` to set property values if that expectation is ever met. You can read more about :ref:`expectations-setting-public-properties`. .. note:: In general, Mockery does not support mocking any magic methods since these are generally not considered a public API (and besides it is a bit difficult to differentiate them when you badly need them for mocking!). So please mock virtual properties (those relying on ``__get()`` and ``__set()``) as if they were actually declared on the class. PKGiZyddocs/reference/index.rstnuW+AReference ========= .. toctree:: :hidden: creating_test_doubles expectations argument_validation alternative_should_receive_syntax spies instance_mocking partial_mocks protected_methods public_properties public_static_properties pass_by_reference_behaviours demeter_chains final_methods_classes magic_methods phpunit_integration .. include:: map.rst.inc PKGiZDҼJ>J>docs/reference/expectations.rstnuW+A.. index:: single: Expectations Expectation Declarations ======================== .. note:: In order for our expectations to work we MUST call ``Mockery::close()``, preferably in a callback method such as ``tearDown`` or ``_after`` (depending on whether or not we're integrating Mockery with another framework). This static call cleans up the Mockery container used by the current test, and run any verification tasks needed for our expectations. Once we have created a mock object, we'll often want to start defining how exactly it should behave (and how it should be called). This is where the Mockery expectation declarations take over. Declaring Method Call Expectations ---------------------------------- To tell our test double to expect a call for a method with a given name, we use the ``shouldReceive`` method: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method'); This is the starting expectation upon which all other expectations and constraints are appended. We can declare more than one method call to be expected: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method_1', 'name_of_method_2'); All of these will adopt any chained expectations or constraints. It is possible to declare the expectations for the method calls, along with their return values: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive([ 'name_of_method_1' => 'return value 1', 'name_of_method_2' => 'return value 2', ]); There's also a shorthand way of setting up method call expectations and their return values: .. code-block:: php $mock = \Mockery::mock('MyClass', ['name_of_method_1' => 'return value 1', 'name_of_method_2' => 'return value 2']); All of these will adopt any additional chained expectations or constraints. We can declare that a test double should not expect a call to the given method name: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldNotReceive('name_of_method'); This method is a convenience method for calling ``shouldReceive()->never()``. Declaring Method Argument Expectations -------------------------------------- For every method we declare expectation for, we can add constraints that the defined expectations apply only to the method calls that match the expected argument list: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->with($arg1, $arg2, ...); // or $mock->shouldReceive('name_of_method') ->withArgs([$arg1, $arg2, ...]); We can add a lot more flexibility to argument matching using the built in matcher classes (see later). For example, ``\Mockery::any()`` matches any argument passed to that position in the ``with()`` parameter list. Mockery also allows Hamcrest library matchers - for example, the Hamcrest function ``anything()`` is equivalent to ``\Mockery::any()``. It's important to note that this means all expectations attached only apply to the given method when it is called with these exact arguments: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo')->with('Hello'); $mock->foo('Goodbye'); // throws a NoMatchingExpectationException This allows for setting up differing expectations based on the arguments provided to expected calls. Argument matching with closures ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Instead of providing a built-in matcher for each argument, we can provide a closure that matches all passed arguments at once: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->withArgs(closure); The given closure receives all the arguments passed in the call to the expected method. In this way, this expectation only applies to method calls where passed arguments make the closure evaluate to true: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo')->withArgs(function ($arg) { if ($arg % 2 == 0) { return true; } return false; }); $mock->foo(4); // matches the expectation $mock->foo(3); // throws a NoMatchingExpectationException Argument matching with some of given values ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We can provide expected arguments that match passed arguments when mocked method is called. .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->withSomeOfArgs(arg1, arg2, arg3, ...); The given expected arguments order doesn't matter. Check if expected values are included or not, but type should be matched: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->withSomeOfArgs(1, 2); $mock->foo(1, 2, 3); // matches the expectation $mock->foo(3, 2, 1); // matches the expectation (passed order doesn't matter) $mock->foo('1', '2'); // throws a NoMatchingExpectationException (type should be matched) $mock->foo(3); // throws a NoMatchingExpectationException Any, or no arguments ^^^^^^^^^^^^^^^^^^^^ We can declare that the expectation matches a method call regardless of what arguments are passed: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->withAnyArgs(); This is set by default unless otherwise specified. We can declare that the expectation matches method calls with zero arguments: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->withNoArgs(); Declaring Return Value Expectations ----------------------------------- For mock objects, we can tell Mockery what return values to return from the expected method calls. For that we can use the ``andReturn()`` method: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturn($value); This sets a value to be returned from the expected method call. It is possible to set up expectation for multiple return values. By providing a sequence of return values, we tell Mockery what value to return on every subsequent call to the method: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturn($value1, $value2, ...) The first call will return ``$value1`` and the second call will return ``$value2``. If we call the method more times than the number of return values we declared, Mockery will return the final value for any subsequent method call: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo')->andReturn(1, 2, 3); $mock->foo(); // int(1) $mock->foo(); // int(2) $mock->foo(); // int(3) $mock->foo(); // int(3) The same can be achieved using the alternative syntax: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturnValues([$value1, $value2, ...]) It accepts a simple array instead of a list of parameters. The order of return is determined by the numerical index of the given array with the last array member being returned on all calls once previous return values are exhausted. The following two options are primarily for communication with test readers: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturnNull(); // or $mock->shouldReceive('name_of_method') ->andReturn([null]); They mark the mock object method call as returning ``null`` or nothing. Sometimes we want to calculate the return results of the method calls, based on the arguments passed to the method. We can do that with the ``andReturnUsing()`` method which accepts one or more closure: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturnUsing(closure, ...); Closures can be queued by passing them as extra parameters as for ``andReturn()``. Occasionally, it can be useful to echo back one of the arguments that a method is called with. In this case we can use the ``andReturnArg()`` method; the argument to be returned is specified by its index in the arguments list: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturnArg(1); This returns the second argument (index #1) from the list of arguments when the method is called. .. note:: We cannot currently mix ``andReturnUsing()`` or ``andReturnArg`` with ``andReturn()``. If we are mocking fluid interfaces, the following method will be helpful: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andReturnSelf(); It sets the return value to the mocked class name. Throwing Exceptions ------------------- We can tell the method of mock objects to throw exceptions: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andThrow(new Exception); It will throw the given ``Exception`` object when called. Rather than an object, we can pass in the ``Exception`` class, message and/or code to use when throwing an ``Exception`` from the mocked method: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andThrow('exception_name', 'message', 123456789); .. _expectations-setting-public-properties: Setting Public Properties ------------------------- Used with an expectation so that when a matching method is called, we can cause a mock object's public property to be set to a specified value, by using ``andSet()`` or ``set()``: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->andSet($property, $value); // or $mock->shouldReceive('name_of_method') ->set($property, $value); In cases where we want to call the real method of the class that was mocked and return its result, the ``passthru()`` method tells the expectation to bypass a return queue: .. code-block:: php passthru() It allows expectation matching and call count validation to be applied against real methods while still calling the real class method with the expected arguments. Declaring Call Count Expectations --------------------------------- Besides setting expectations on the arguments of the method calls, and the return values of those same calls, we can set expectations on how many times should any method be called. When a call count expectation is not met, a ``\Mockery\Expectation\InvalidCountException`` will be thrown. .. note:: It is absolutely required to call ``\Mockery::close()`` at the end of our tests, for example in the ``tearDown()`` method of PHPUnit. Otherwise Mockery will not verify the calls made against our mock objects. We can declare that the expected method may be called zero or more times: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->zeroOrMoreTimes(); This is the default for all methods unless otherwise set. To tell Mockery to expect an exact number of calls to a method, we can use the following: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->times($n); where ``$n`` is the number of times the method should be called. A couple of most common cases got their shorthand methods. To declare that the expected method must be called one time only: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->once(); To declare that the expected method must be called two times: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->twice(); To declare that the expected method must never be called: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->never(); Call count modifiers ^^^^^^^^^^^^^^^^^^^^ The call count expectations can have modifiers set. If we want to tell Mockery the minimum number of times a method should be called, we use ``atLeast()``: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->atLeast() ->times(3); ``atLeast()->times(3)`` means the call must be called at least three times (given matching method args) but never less than three times. Similarly, we can tell Mockery the maximum number of times a method should be called, using ``atMost()``: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->atMost() ->times(3); ``atMost()->times(3)`` means the call must be called no more than three times. If the method gets no calls at all, the expectation will still be met. We can also set a range of call counts, using ``between()``: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('name_of_method') ->between($min, $max); This is actually identical to using ``atLeast()->times($min)->atMost()->times($max)`` but is provided as a shorthand. It may be followed by a ``times()`` call with no parameter to preserve the APIs natural language readability. Multiple Calls with Different Expectations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If a method is expected to get called multiple times with different arguments and/or return values we can simply repeat the expectations. The same of course also works if we expect multiple calls to different methods. .. code-block:: php $mock = \Mockery::mock('MyClass'); // Expectations for the 1st call $mock->shouldReceive('name_of_method') ->once() ->with('arg1') ->andReturn($value1) // 2nd call to same method ->shouldReceive('name_of_method') ->once() ->with('arg2') ->andReturn($value2) // final call to another method ->shouldReceive('other_method') ->once() ->with('other') ->andReturn($value_other); Expectation Declaration Utilities --------------------------------- Declares that this method is expected to be called in a specific order in relation to similarly marked methods. .. code-block:: php ordered() The order is dictated by the order in which this modifier is actually used when setting up mocks. Declares the method as belonging to an order group (which can be named or numbered). Methods within a group can be called in any order, but the ordered calls from outside the group are ordered in relation to the group: .. code-block:: php ordered(group) We can set up so that method1 is called before group1 which is in turn called before method2. When called prior to ``ordered()`` or ``ordered(group)``, it declares this ordering to apply across all mock objects (not just the current mock): .. code-block:: php globally() This allows for dictating order expectations across multiple mocks. The ``byDefault()`` marks an expectation as a default. Default expectations are applied unless a non-default expectation is created: .. code-block:: php byDefault() These later expectations immediately replace the previously defined default. This is useful so we can setup default mocks in our unit test ``setup()`` and later tweak them in specific tests as needed. Returns the current mock object from an expectation chain: .. code-block:: php getMock() Useful where we prefer to keep mock setups as a single statement, e.g.: .. code-block:: php $mock = \Mockery::mock('foo')->shouldReceive('foo')->andReturn(1)->getMock(); PKGiZZLig/docs/reference/pass_by_reference_behaviours.rstnuW+A.. index:: single: Pass-By-Reference Method Parameter Behaviour Preserving Pass-By-Reference Method Parameter Behaviour ======================================================= PHP Class method may accept parameters by reference. In this case, changes made to the parameter (a reference to the original variable passed to the method) are reflected in the original variable. An example: .. code-block:: php class Foo { public function bar(&$a) { $a++; } } $baz = 1; $foo = new Foo; $foo->bar($baz); echo $baz; // will echo the integer 2 In the example above, the variable ``$baz`` is passed by reference to ``Foo::bar()`` (notice the ``&`` symbol in front of the parameter?). Any change ``bar()`` makes to the parameter reference is reflected in the original variable, ``$baz``. Mockery handles references correctly for all methods where it can analyse the parameter (using ``Reflection``) to see if it is passed by reference. To mock how a reference is manipulated by the class method, we can use a closure argument matcher to manipulate it, i.e. ``\Mockery::on()`` - see the :ref:`argument-validation-complex-argument-validation` chapter. There is an exception for internal PHP classes where Mockery cannot analyse method parameters using ``Reflection`` (a limitation in PHP). To work around this, we can explicitly declare method parameters for an internal class using ``\Mockery\Configuration::setInternalClassMethodParamMap()``. Here's an example using ``MongoCollection::insert()``. ``MongoCollection`` is an internal class offered by the mongo extension from PECL. Its ``insert()`` method accepts an array of data as the first parameter, and an optional options array as the second parameter. The original data array is updated (i.e. when a ``insert()`` pass-by-reference parameter) to include a new ``_id`` field. We can mock this behaviour using a configured parameter map (to tell Mockery to expect a pass by reference parameter) and a ``Closure`` attached to the expected method parameter to be updated. Here's a PHPUnit unit test verifying that this pass-by-reference behaviour is preserved: .. code-block:: php public function testCanOverrideExpectedParametersOfInternalPHPClassesToPreserveRefs() { \Mockery::getConfiguration()->setInternalClassMethodParamMap( 'MongoCollection', 'insert', array('&$data', '$options = array()') ); $m = \Mockery::mock('MongoCollection'); $m->shouldReceive('insert')->with( \Mockery::on(function(&$data) { if (!is_array($data)) return false; $data['_id'] = 123; return true; }), \Mockery::any() ); $data = array('a'=>1,'b'=>2); $m->insert($data); $this->assertTrue(isset($data['_id'])); $this->assertEquals(123, $data['_id']); \Mockery::resetContainer(); } Protected Methods ----------------- When dealing with protected methods, and trying to preserve pass by reference behavior for them, a different approach is required. .. code-block:: php class Model { public function test(&$data) { return $this->doTest($data); } protected function doTest(&$data) { $data['something'] = 'wrong'; return $this; } } class Test extends \PHPUnit\Framework\TestCase { public function testModel() { $mock = \Mockery::mock('Model[test]')->shouldAllowMockingProtectedMethods(); $mock->shouldReceive('test') ->with(\Mockery::on(function(&$data) { $data['something'] = 'wrong'; return true; })); $data = array('foo' => 'bar'); $mock->test($data); $this->assertTrue(isset($data['something'])); $this->assertEquals('wrong', $data['something']); } } This is quite an edge case, so we need to change the original code a little bit, by creating a public method that will call our protected method, and then mock that, instead of the protected method. This new public method will act as a proxy to our protected method. PKGiZG$docs/reference/protected_methods.rstnuW+A.. index:: single: Mocking; Protected Methods Mocking Protected Methods ========================= By default, Mockery does not allow mocking protected methods. We do not recommend mocking protected methods, but there are cases when there is no other solution. For those cases we have the ``shouldAllowMockingProtectedMethods()`` method. It instructs Mockery to specifically allow mocking of protected methods, for that one class only: .. code-block:: php class MyClass { protected function foo() { } } $mock = \Mockery::mock('MyClass') ->shouldAllowMockingProtectedMethods(); $mock->shouldReceive('foo'); PKGiZ03!))&docs/reference/argument_validation.rstnuW+A.. index:: single: Argument Validation Argument Validation =================== The arguments passed to the ``with()`` declaration when setting up an expectation determine the criteria for matching method calls to expectations. Thus, we can setup up many expectations for a single method, each differentiated by the expected arguments. Such argument matching is done on a "best fit" basis. This ensures explicit matches take precedence over generalised matches. An explicit match is merely where the expected argument and the actual argument are easily equated (i.e. using ``===`` or ``==``). More generalised matches are possible using regular expressions, class hinting and the available generic matchers. The purpose of generalised matchers is to allow arguments be defined in non-explicit terms, e.g. ``Mockery::any()`` passed to ``with()`` will match **any** argument in that position. Mockery's generic matchers do not cover all possibilities but offers optional support for the Hamcrest library of matchers. Hamcrest is a PHP port of the similarly named Java library (which has been ported also to Python, Erlang, etc). By using Hamcrest, Mockery does not need to duplicate Hamcrest's already impressive utility which itself promotes a natural English DSL. The examples below show Mockery matchers and their Hamcrest equivalent, if there is one. Hamcrest uses functions (no namespacing). .. note:: If you don't wish to use the global Hamcrest functions, they are all exposed through the ``\Hamcrest\Matchers`` class as well, as static methods. Thus, ``identicalTo($arg)`` is the same as ``\Hamcrest\Matchers::identicalTo($arg)`` The most common matcher is the ``with()`` matcher: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(1): It tells mockery that it should receive a call to the ``foo`` method with the integer ``1`` as an argument. In cases like this, Mockery first tries to match the arguments using ``===`` (identical) comparison operator. If the argument is a primitive, and if it fails the identical comparison, Mockery does a fallback to the ``==`` (equals) comparison operator. When matching objects as arguments, Mockery only does the strict ``===`` comparison, which means only the same ``$object`` will match: .. code-block:: php $object = new stdClass(); $mock = \Mockery::mock('MyClass'); $mock->shouldReceive("foo") ->with($object); // Hamcrest equivalent $mock->shouldReceive("foo") ->with(identicalTo($object)); A different instance of ``stdClass`` will **not** match. .. note:: The ``Mockery\Matcher\MustBe`` matcher has been deprecated. If we need a loose comparison of objects, we can do that using Hamcrest's ``equalTo`` matcher: .. code-block:: php $mock->shouldReceive("foo") ->with(equalTo(new stdClass)); In cases when we don't care about the type, or the value of an argument, just that any argument is present, we use ``any()``: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive("foo") ->with(\Mockery::any()); // Hamcrest equivalent $mock->shouldReceive("foo") ->with(anything()) Anything and everything passed in this argument slot is passed unconstrained. Validating Types and Resources ------------------------------ The ``type()`` matcher accepts any string which can be attached to ``is_`` to form a valid type check. To match any PHP resource, we could do the following: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive("foo") ->with(\Mockery::type('resource')); // Hamcrest equivalents $mock->shouldReceive("foo") ->with(resourceValue()); $mock->shouldReceive("foo") ->with(typeOf('resource')); It will return a ``true`` from an ``is_resource()`` call, if the provided argument to the method is a PHP resource. For example, ``\Mockery::type('float')`` or Hamcrest's ``floatValue()`` and ``typeOf('float')`` checks use ``is_float()``, and ``\Mockery::type('callable')`` or Hamcrest's ``callable()`` uses ``is_callable()``. The ``type()`` matcher also accepts a class or interface name to be used in an ``instanceof`` evaluation of the actual argument. Hamcrest uses ``anInstanceOf()``. A full list of the type checkers is available at `php.net `_ or browse Hamcrest's function list in `the Hamcrest code `_. .. _argument-validation-complex-argument-validation: Complex Argument Validation --------------------------- If we want to perform a complex argument validation, the ``on()`` matcher is invaluable. It accepts a closure (anonymous function) to which the actual argument will be passed. .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive("foo") ->with(\Mockery::on(closure)); If the closure evaluates to (i.e. returns) boolean ``true`` then the argument is assumed to have matched the expectation. .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::on(function ($argument) { if ($argument % 2 == 0) { return true; } return false; })); $mock->foo(4); // matches the expectation $mock->foo(3); // throws a NoMatchingExpectationException .. note:: There is no Hamcrest version of the ``on()`` matcher. We can also perform argument validation by passing a closure to ``withArgs()`` method. The closure will receive all arguments passed in the call to the expected method and if it evaluates (i.e. returns) to boolean ``true``, then the list of arguments is assumed to have matched the expectation: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive("foo") ->withArgs(closure); The closure can also handle optional parameters, so if an optional parameter is missing in the call to the expected method, it doesn't necessary means that the list of arguments doesn't match the expectation. .. code-block:: php $closure = function ($odd, $even, $sum = null) { $result = ($odd % 2 != 0) && ($even % 2 == 0); if (!is_null($sum)) { return $result && ($odd + $even == $sum); } return $result; }; $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo')->withArgs($closure); $mock->foo(1, 2); // It matches the expectation: the optional argument is not needed $mock->foo(1, 2, 3); // It also matches the expectation: the optional argument pass the validation $mock->foo(1, 2, 4); // It doesn't match the expectation: the optional doesn't pass the validation .. note:: In previous versions, Mockery's ``with()`` would attempt to do a pattern matching against the arguments, attempting to use the argument as a regular expression. Over time this proved to be not such a great idea, so we removed this functionality, and have introduced ``Mockery::pattern()`` instead. If we would like to match an argument against a regular expression, we can use the ``\Mockery::pattern()``: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::pattern('/^foo/')); // Hamcrest equivalent $mock->shouldReceive('foo') ->with(matchesPattern('/^foo/')); The ``ducktype()`` matcher is an alternative to matching by class type: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::ducktype('foo', 'bar')); It matches any argument which is an object containing the provided list of methods to call. .. note:: There is no Hamcrest version of the ``ducktype()`` matcher. Capturing Arguments ------------------- If we want to perform multiple validations on a single argument, the ``capture`` matcher provides a streamlined alternative to using the ``on()`` matcher. It accepts a variable which the actual argument will be assigned. .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive("foo") ->with(\Mockery::capture($bar)); This will assign *any* argument passed to ``foo`` to the local ``$bar`` variable to then perform additional validation using assertions. .. note:: The ``capture`` matcher always evaluates to ``true``. As such, we should always perform additional argument validation. Additional Argument Matchers ---------------------------- The ``not()`` matcher matches any argument which is not equal or identical to the matcher's parameter: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::not(2)); // Hamcrest equivalent $mock->shouldReceive('foo') ->with(not(2)); ``anyOf()`` matches any argument which equals any one of the given parameters: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::anyOf(1, 2)); // Hamcrest equivalent $mock->shouldReceive('foo') ->with(anyOf(1,2)); ``notAnyOf()`` matches any argument which is not equal or identical to any of the given parameters: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::notAnyOf(1, 2)); .. note:: There is no Hamcrest version of the ``notAnyOf()`` matcher. ``subset()`` matches any argument which is any array containing the given array subset: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::subset(array(0 => 'foo'))); This enforces both key naming and values, i.e. both the key and value of each actual element is compared. .. note:: There is no Hamcrest version of this functionality, though Hamcrest can check a single entry using ``hasEntry()`` or ``hasKeyValuePair()``. ``contains()`` matches any argument which is an array containing the listed values: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::contains(value1, value2)); The naming of keys is ignored. ``hasKey()`` matches any argument which is an array containing the given key name: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::hasKey(key)); ``hasValue()`` matches any argument which is an array containing the given value: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->with(\Mockery::hasValue(value)); PKGiZϬmGG(docs/reference/final_methods_classes.rstnuW+A.. index:: single: Mocking; Final Classes/Methods Dealing with Final Classes/Methods ================================== One of the primary restrictions of mock objects in PHP, is that mocking classes or methods marked final is hard. The final keyword prevents methods so marked from being replaced in subclasses (subclassing is how mock objects can inherit the type of the class or object being mocked). The simplest solution is to implement an interface in your final class and typehint against / mock this. However this may not be possible in some third party libraries. Mockery does allow creating "proxy mocks" from classes marked final, or from classes with methods marked final. This offers all the usual mock object goodness but the resulting mock will not inherit the class type of the object being mocked, i.e. it will not pass any instanceof comparison. Methods marked as final will be proxied to the original method, i.e., final methods can't be mocked. We can create a proxy mock by passing the instantiated object we wish to mock into ``\Mockery::mock()``, i.e. Mockery will then generate a Proxy to the real object and selectively intercept method calls for the purposes of setting and meeting expectations. See the :ref:`creating-test-doubles-partial-test-doubles` chapter, the subsection about proxied partial test doubles. PKGiZ4%$docs/reference/spies.rstnuW+A.. index:: single: Reference; Spies Spies ===== Spies are a type of test doubles, but they differ from stubs or mocks in that, that the spies record any interaction between the spy and the System Under Test (SUT), and allow us to make assertions against those interactions after the fact. Creating a spy means we don't have to set up expectations for every method call the double might receive during the test, some of which may not be relevant to the current test. A spy allows us to make assertions about the calls we care about for this test only, reducing the chances of over-specification and making our tests more clear. Spies also allow us to follow the more familiar Arrange-Act-Assert or Given-When-Then style within our tests. With mocks, we have to follow a less familiar style, something along the lines of Arrange-Expect-Act-Assert, where we have to tell our mocks what to expect before we act on the SUT, then assert that those expectations were met: .. code-block:: php // arrange $mock = \Mockery::mock('MyDependency'); $sut = new MyClass($mock); // expect $mock->shouldReceive('foo') ->once() ->with('bar'); // act $sut->callFoo(); // assert \Mockery::close(); Spies allow us to skip the expect part and move the assertion to after we have acted on the SUT, usually making our tests more readable: .. code-block:: php // arrange $spy = \Mockery::spy('MyDependency'); $sut = new MyClass($spy); // act $sut->callFoo(); // assert $spy->shouldHaveReceived() ->foo() ->with('bar'); On the other hand, spies are far less restrictive than mocks, meaning tests are usually less precise, as they let us get away with more. This is usually a good thing, they should only be as precise as they need to be, but while spies make our tests more intent-revealing, they do tend to reveal less about the design of the SUT. If we're having to setup lots of expectations for a mock, in lots of different tests, our tests are trying to tell us something - the SUT is doing too much and probably should be refactored. We don't get this with spies, they simply ignore the calls that aren't relevant to them. Another downside to using spies is debugging. When a mock receives a call that it wasn't expecting, it immediately throws an exception (failing fast), giving us a nice stack trace or possibly even invoking our debugger. With spies, we're simply asserting calls were made after the fact, so if the wrong calls were made, we don't have quite the same just in time context we have with the mocks. Finally, if we need to define a return value for our test double, we can't do that with a spy, only with a mock object. .. note:: This documentation page is an adaption of the blog post titled `"Mockery Spies" `_, published by Dave Marshall on his blog. Dave is the original author of spies in Mockery. Spies Reference --------------- To verify that a method was called on a spy, we use the ``shouldHaveReceived()`` method: .. code-block:: php $spy->shouldHaveReceived('foo'); To verify that a method was **not** called on a spy, we use the ``shouldNotHaveReceived()`` method: .. code-block:: php $spy->shouldNotHaveReceived('foo'); We can also do argument matching with spies: .. code-block:: php $spy->shouldHaveReceived('foo') ->with('bar'); Argument matching is also possible by passing in an array of arguments to match: .. code-block:: php $spy->shouldHaveReceived('foo', ['bar']); Although when verifying a method was not called, the argument matching can only be done by supplying the array of arguments as the 2nd argument to the ``shouldNotHaveReceived()`` method: .. code-block:: php $spy->shouldNotHaveReceived('foo', ['bar']); This is due to Mockery's internals. Finally, when expecting calls that should have been received, we can also verify the number of calls: .. code-block:: php $spy->shouldHaveReceived('foo') ->with('bar') ->twice(); Alternative shouldReceive syntax ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As of Mockery 1.0.0, we support calling methods as we would call any PHP method, and not as string arguments to Mockery ``should*`` methods. In cases of spies, this only applies to the ``shouldHaveReceived()`` method: .. code-block:: php $spy->shouldHaveReceived() ->foo('bar'); We can set expectation on number of calls as well: .. code-block:: php $spy->shouldHaveReceived() ->foo('bar') ->twice(); Unfortunately, due to limitations we can't support the same syntax for the ``shouldNotHaveReceived()`` method. PKGiZp docs/reference/magic_methods.rstnuW+A.. index:: single: Mocking; Magic Methods PHP Magic Methods ================= PHP magic methods which are prefixed with a double underscore, e.g. ``__set()``, pose a particular problem in mocking and unit testing in general. It is strongly recommended that unit tests and mock objects do not directly refer to magic methods. Instead, refer only to the virtual methods and properties these magic methods simulate. Following this piece of advice will ensure we are testing the real API of classes and also ensures there is no conflict should Mockery override these magic methods, which it will inevitably do in order to support its role in intercepting method calls and properties. PKGiZ$docs/index.rstnuW+AMockery ======= Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending. Mock Objects ------------ In unit tests, mock objects simulate the behaviour of real objects. They are commonly utilised to offer test isolation, to stand in for objects which do not yet exist, or to allow for the exploratory design of class APIs without requiring actual implementation up front. The benefits of a mock object framework are to allow for the flexible generation of such mock objects (and stubs). They allow the setting of expected method calls and return values using a flexible API which is capable of capturing every possible real object behaviour in way that is stated as close as possible to a natural language description. Getting Started --------------- Ready to dive into the Mockery framework? Then you can get started by reading the "Getting Started" section! .. toctree:: :hidden: getting_started/index .. include:: getting_started/map.rst.inc Reference --------- The reference contains a complete overview of all features of the Mockery framework. .. toctree:: :hidden: reference/index .. include:: reference/map.rst.inc Mockery ------- Learn about Mockery's configuration, reserved method names, exceptions... .. toctree:: :hidden: mockery/index .. include:: mockery/map.rst.inc Cookbook -------- Want to learn some easy tips and tricks? Take a look at the cookbook articles! .. toctree:: :hidden: cookbook/index .. include:: cookbook/map.rst.inc PKGiZ>(ETTdocs/README.mdnuW+Amockery-docs ============ Document for the PHP Mockery framework on readthedocs.orgPKGiZul% docs/getting_started/map.rst.incnuW+A* :doc:`/getting_started/installation` * :doc:`/getting_started/upgrading` * :doc:`/getting_started/simple_example` * :doc:`/getting_started/quick_reference` PKGiZ]b!%docs/getting_started/installation.rstnuW+A.. index:: single: Installation Installation ============ Mockery can be installed using Composer or by cloning it from its GitHub repository. These two options are outlined below. Composer -------- You can read more about Composer on `getcomposer.org `_. To install Mockery using Composer, first install Composer for your project using the instructions on the `Composer download page `_. You can then define your development dependency on Mockery using the suggested parameters below. While every effort is made to keep the master branch stable, you may prefer to use the current stable version tag instead (use the ``@stable`` tag). .. code-block:: json { "require-dev": { "mockery/mockery": "dev-master" } } To install, you then may call: .. code-block:: bash php composer.phar update This will install Mockery as a development dependency, meaning it won't be installed when using ``php composer.phar update --no-dev`` in production. Other way to install is directly from composer command line, as below. .. code-block:: bash php composer.phar require --dev mockery/mockery Git --- The Git repository hosts the development version in its master branch. You can install this using Composer by referencing ``dev-master`` as your preferred version in your project's ``composer.json`` file as the earlier example shows. PKGiZH,docs/getting_started/index.rstnuW+AGetting Started =============== .. toctree:: :hidden: installation upgrading simple_example quick_reference .. include:: map.rst.inc PKGiZcservice = $service; } public function average() { $total = 0; for ($i=0; $i<3; $i++) { $total += $this->service->readTemp(); } return $total/3; } } Even without an actual service class, we can see how we expect it to operate. When writing a test for the ``Temperature`` class, we can now substitute a mock object for the real service which allows us to test the behaviour of the ``Temperature`` class without actually needing a concrete service instance. .. code-block:: php use \Mockery; class TemperatureTest extends \PHPUnit\Framework\TestCase { public function tearDown() { Mockery::close(); } public function testGetsAverageTemperatureFromThreeServiceReadings() { $service = Mockery::mock('service'); $service->shouldReceive('readTemp') ->times(3) ->andReturn(10, 12, 14); $temperature = new Temperature($service); $this->assertEquals(12, $temperature->average()); } } We create a mock object which our ``Temperature`` class will use and set some expectations for that mock — that it should receive three calls to the ``readTemp`` method, and these calls will return 10, 12, and 14 as results. .. note:: PHPUnit integration can remove the need for a ``tearDown()`` method. See ":doc:`/reference/phpunit_integration`" for more information. PKGiZ5X(docs/getting_started/quick_reference.rstnuW+A.. index:: single: Quick Reference Quick Reference =============== The purpose of this page is to give a quick and short overview of some of the most common Mockery features. Do read the :doc:`../reference/index` to learn about all the Mockery features. Integrate Mockery with PHPUnit, either by extending the ``MockeryTestCase``: .. code-block:: php use \Mockery\Adapter\Phpunit\MockeryTestCase; class MyTest extends MockeryTestCase { } or by using the ``MockeryPHPUnitIntegration`` trait: .. code-block:: php use \PHPUnit\Framework\TestCase; use \Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; class MyTest extends TestCase { use MockeryPHPUnitIntegration; } Creating a test double: .. code-block:: php $testDouble = \Mockery::mock('MyClass'); Creating a test double that implements a certain interface: .. code-block:: php $testDouble = \Mockery::mock('MyClass, MyInterface'); Expecting a method to be called on a test double: .. code-block:: php $testDouble = \Mockery::mock('MyClass'); $testDouble->shouldReceive('foo'); Expecting a method to **not** be called on a test double: .. code-block:: php $testDouble = \Mockery::mock('MyClass'); $testDouble->shouldNotReceive('foo'); Expecting a method to be called on a test double, once, with a certain argument, and to return a value: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->once() ->with($arg) ->andReturn($returnValue); Expecting a method to be called on a test double and to return a different value for each successive call: .. code-block:: php $mock = \Mockery::mock('MyClass'); $mock->shouldReceive('foo') ->andReturn(1, 2, 3); $mock->foo(); // int(1); $mock->foo(); // int(2); $mock->foo(); // int(3); $mock->foo(); // int(3); Creating a runtime partial test double: .. code-block:: php $mock = \Mockery::mock('MyClass')->makePartial(); Creating a spy: .. code-block:: php $spy = \Mockery::spy('MyClass'); Expecting that a spy should have received a method call: .. code-block:: php $spy = \Mockery::spy('MyClass'); $spy->foo(); $spy->shouldHaveReceived()->foo(); Not so simple examples ^^^^^^^^^^^^^^^^^^^^^^ Creating a mock object to return a sequence of values from a set of method calls: .. code-block:: php use \Mockery\Adapter\Phpunit\MockeryTestCase; class SimpleTest extends MockeryTestCase { public function testSimpleMock() { $mock = \Mockery::mock(array('pi' => 3.1416, 'e' => 2.71)); $this->assertEquals(3.1416, $mock->pi()); $this->assertEquals(2.71, $mock->e()); } } Creating a mock object which returns a self-chaining Undefined object for a method call: .. code-block:: php use \Mockery\Adapter\Phpunit\MockeryTestCase; class UndefinedTest extends MockeryTestCase { public function testUndefinedValues() { $mock = \Mockery::mock('mymock'); $mock->shouldReceive('divideBy')->with(0)->andReturnUndefined(); $this->assertTrue($mock->divideBy(0) instanceof \Mockery\Undefined); } } Creating a mock object with multiple query calls and a single update call: .. code-block:: php use \Mockery\Adapter\Phpunit\MockeryTestCase; class DbTest extends MockeryTestCase { public function testDbAdapter() { $mock = \Mockery::mock('db'); $mock->shouldReceive('query')->andReturn(1, 2, 3); $mock->shouldReceive('update')->with(5)->andReturn(NULL)->once(); // ... test code here using the mock } } Expecting all queries to be executed before any updates: .. code-block:: php use \Mockery\Adapter\Phpunit\MockeryTestCase; class DbTest extends MockeryTestCase { public function testQueryAndUpdateOrder() { $mock = \Mockery::mock('db'); $mock->shouldReceive('query')->andReturn(1, 2, 3)->ordered(); $mock->shouldReceive('update')->andReturn(NULL)->once()->ordered(); // ... test code here using the mock } } Creating a mock object where all queries occur after startup, but before finish, and where queries are expected with several different params: .. code-block:: php use \Mockery\Adapter\Phpunit\MockeryTestCase; class DbTest extends MockeryTestCase { public function testOrderedQueries() { $db = \Mockery::mock('db'); $db->shouldReceive('startup')->once()->ordered(); $db->shouldReceive('query')->with('CPWR')->andReturn(12.3)->once()->ordered('queries'); $db->shouldReceive('query')->with('MSFT')->andReturn(10.0)->once()->ordered('queries'); $db->shouldReceive('query')->with(\Mockery::pattern("/^....$/"))->andReturn(3.3)->atLeast()->once()->ordered('queries'); $db->shouldReceive('finish')->once()->ordered(); // ... test code here using the mock } } PKGiZY "docs/getting_started/upgrading.rstnuW+A.. index:: single: Upgrading Upgrading ========= Upgrading to 1.0.0 ------------------ Minimum PHP version +++++++++++++++++++ As of Mockery 1.0.0 the minimum PHP version required is 5.6. Using Mockery with PHPUnit ++++++++++++++++++++++++++ In the "old days", 0.9.x and older, the way Mockery was integrated with PHPUnit was through a PHPUnit listener. That listener would in turn call the ``\Mockery::close()`` method for us. As of 1.0.0, PHPUnit test cases where we want to use Mockery, should either use the ``\Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration`` trait, or extend the ``\Mockery\Adapter\Phpunit\MockeryTestCase`` test case. This will in turn call the ``\Mockery::close()`` method for us. Read the documentation for a detailed overview of ":doc:`/reference/phpunit_integration`". ``\Mockery\Matcher\MustBe`` is deprecated +++++++++++++++++++++++++++++++++++++++++ As of 1.0.0 the ``\Mockery\Matcher\MustBe`` matcher is deprecated and will be removed in Mockery 2.0.0. We recommend instead to use the PHPUnit equivalents of the MustBe matcher. ``allows`` and ``expects`` ++++++++++++++++++++++++++ As of 1.0.0, Mockery has two new methods to set up expectations: ``allows`` and ``expects``. This means that these methods names are now "reserved" for Mockery, or in other words classes you want to mock with Mockery, can't have methods called ``allows`` or ``expects``. Read more in the documentation about this ":doc:`/reference/alternative_should_receive_syntax`". No more implicit regex matching for string arguments ++++++++++++++++++++++++++++++++++++++++++++++++++++ When setting up string arguments in method expectations, Mockery 0.9.x and older, would try to match arguments using a regular expression in a "last attempt" scenario. As of 1.0.0, Mockery will no longer attempt to do this regex matching, but will only try first the identical operator ``===``, and failing that, the equals operator ``==``. If you want to match an argument using regular expressions, please use the new ``\Mockery\Matcher\Pattern`` matcher. Read more in the documentation about this pattern matcher in the ":doc:`/reference/argument_validation`" section. ``andThrow`` ``\Throwable`` +++++++++++++++++++++++++++ As of 1.0.0, the ``andThrow`` can now throw any ``\Throwable``. Upgrading to 0.9 ---------------- The generator was completely rewritten, so any code with a deep integration to mockery will need evaluating. Upgrading to 0.8 ---------------- Since the release of 0.8.0 the following behaviours were altered: 1. The ``shouldIgnoreMissing()`` behaviour optionally applied to mock objects returned an instance of ``\Mockery\Undefined`` when methods called did not match a known expectation. Since 0.8.0, this behaviour was switched to returning ``null`` instead. You can restore the 0.7.2 behaviour by using the following: .. code-block:: php $mock = \Mockery::mock('stdClass')->shouldIgnoreMissing()->asUndefined(); PKGiZwDUdocs/mockery/map.rst.incnuW+A* :doc:`/mockery/configuration` * :doc:`/mockery/exceptions` * :doc:`/mockery/reserved_method_names` * :doc:`/mockery/gotchas` PKGiZ :edocs/mockery/index.rstnuW+AMockery ======= .. toctree:: :hidden: configuration exceptions reserved_method_names gotchas .. include:: map.rst.inc PKGiZ_o o docs/mockery/exceptions.rstnuW+A.. index:: single: Mockery; Exceptions Mockery Exceptions ================== Mockery throws three types of exceptions when it cannot verify a mock object. #. ``\Mockery\Exception\InvalidCountException`` #. ``\Mockery\Exception\InvalidOrderException`` #. ``\Mockery\Exception\NoMatchingExpectationException`` You can capture any of these exceptions in a try...catch block to query them for specific information which is also passed along in the exception message but is provided separately from getters should they be useful when logging or reformatting output. \Mockery\Exception\InvalidCountException ---------------------------------------- The exception class is used when a method is called too many (or too few) times and offers the following methods: * ``getMock()`` - return actual mock object * ``getMockName()`` - return the name of the mock object * ``getMethodName()`` - return the name of the method the failing expectation is attached to * ``getExpectedCount()`` - return expected calls * ``getExpectedCountComparative()`` - returns a string, e.g. ``<=`` used to compare to actual count * ``getActualCount()`` - return actual calls made with given argument constraints \Mockery\Exception\InvalidOrderException ---------------------------------------- The exception class is used when a method is called outside the expected order set using the ``ordered()`` and ``globally()`` expectation modifiers. It offers the following methods: * ``getMock()`` - return actual mock object * ``getMockName()`` - return the name of the mock object * ``getMethodName()`` - return the name of the method the failing expectation is attached to * ``getExpectedOrder()`` - returns an integer represented the expected index for which this call was expected * ``getActualOrder()`` - return the actual index at which this method call occurred. \Mockery\Exception\NoMatchingExpectationException ------------------------------------------------- The exception class is used when a method call does not match any known expectation. All expectations are uniquely identified in a mock object by the method name and the list of expected arguments. You can disable this exception and opt for returning NULL from all unexpected method calls by using the earlier mentioned shouldIgnoreMissing() behaviour modifier. This exception class offers the following methods: * ``getMock()`` - return actual mock object * ``getMockName()`` - return the name of the mock object * ``getMethodName()`` - return the name of the method the failing expectation is attached to * ``getActualArguments()`` - return actual arguments used to search for a matching expectation PKGiZ#xUU&docs/mockery/reserved_method_names.rstnuW+A.. index:: single: Reserved Method Names Reserved Method Names ===================== As you may have noticed, Mockery uses a number of methods called directly on all mock objects, for example ``shouldReceive()``. Such methods are necessary in order to setup expectations on the given mock, and so they cannot be implemented on the classes or objects being mocked without creating a method name collision (reported as a PHP fatal error). The methods reserved by Mockery are: * ``shouldReceive()`` * ``shouldNotReceive()`` * ``allows()`` * ``expects()`` * ``shouldAllowMockingMethod()`` * ``shouldIgnoreMissing()`` * ``asUndefined()`` * ``shouldAllowMockingProtectedMethods()`` * ``makePartial()`` * ``byDefault()`` * ``shouldHaveReceived()`` * ``shouldHaveBeenCalled()`` * ``shouldNotHaveReceived()`` * ``shouldNotHaveBeenCalled()`` In addition, all mocks utilise a set of added methods and protected properties which cannot exist on the class or object being mocked. These are far less likely to cause collisions. All properties are prefixed with ``_mockery`` and all method names with ``mockery_``. PKGiZ&#ydocs/mockery/configuration.rstnuW+A.. index:: single: Mockery; Configuration Mockery Global Configuration ============================ To allow for a degree of fine-tuning, Mockery utilises a singleton configuration object to store a small subset of core behaviours. The three currently present include: * Option to allow/disallow the mocking of methods which do not actually exist fulfilled (i.e. unused) * Setter/Getter for added a parameter map for internal PHP class methods (``Reflection`` cannot detect these automatically) * Option to drive if quick definitions should define a stub or a mock with an 'at least once' expectation. By default, the first behaviour is enabled. Of course, there are situations where this can lead to unintended consequences. The mocking of non-existent methods may allow mocks based on real classes/objects to fall out of sync with the actual implementations, especially when some degree of integration testing (testing of object wiring) is not being performed. You may allow or disallow this behaviour (whether for whole test suites or just select tests) by using the following call: .. code-block:: php \Mockery::getConfiguration()->allowMockingNonExistentMethods(bool); Passing a true allows the behaviour, false disallows it. It takes effect immediately until switched back. If the behaviour is detected when not allowed, it will result in an Exception being thrown at that point. Note that disallowing this behaviour should be carefully considered since it necessarily removes at least some of Mockery's flexibility. The other two methods are: .. code-block:: php \Mockery::getConfiguration()->setInternalClassMethodParamMap($class, $method, array $paramMap) \Mockery::getConfiguration()->getInternalClassMethodParamMap($class, $method) These are used to define parameters (i.e. the signature string of each) for the methods of internal PHP classes (e.g. SPL, or PECL extension classes like ext/mongo's MongoCollection. Reflection cannot analyse the parameters of internal classes. Most of the time, you never need to do this. It's mainly needed where an internal class method uses pass-by-reference for a parameter - you MUST in such cases ensure the parameter signature includes the ``&`` symbol correctly as Mockery won't correctly add it automatically for internal classes. Note that internal class parameter overriding is not available in PHP 8. This is because incompatible signatures have been reclassified as fatal errors. Finally there is the possibility to change what a quick definition produces. By default quick definitions create stubs but you can change this behaviour by asking Mockery to use 'at least once' expectations. .. code-block:: php \Mockery::getConfiguration()->getQuickDefinitions()->shouldBeCalledAtLeastOnce(bool) Passing a true allows the behaviour, false disallows it. It takes effect immediately until switched back. By doing so you can avoid the proliferating of quick definitions that accumulate overtime in your code since the test would fail in case the 'at least once' expectation is not fulfilled. Disabling reflection caching ---------------------------- Mockery heavily uses `"reflection" `_ to do it's job. To speed up things, Mockery caches internally the information it gathers via reflection. In some cases, this caching can cause problems. The **only** known situation when this occurs is when PHPUnit's ``--static-backup`` option is used. If you use ``--static-backup`` and you get an error that looks like the following: .. code-block:: php Error: Internal error: Failed to retrieve the reflection object We suggest turning off the reflection cache as so: .. code-block:: php \Mockery::getConfiguration()->disableReflectionCache(); Turning it back on can be done like so: .. code-block:: php \Mockery::getConfiguration()->enableReflectionCache(); In no other situation should you be required turn this reflection cache off. PKGiZY  docs/mockery/gotchas.rstnuW+A.. index:: single: Mockery; Gotchas Gotchas! ======== Mocking objects in PHP has its limitations and gotchas. Some functionality can't be mocked or can't be mocked YET! If you locate such a circumstance, please please (pretty please with sugar on top) create a new issue on GitHub so it can be documented and resolved where possible. Here is a list to note: 1. Classes containing public ``__wakeup()`` methods can be mocked but the mocked ``__wakeup()`` method will perform no actions and cannot have expectations set for it. This is necessary since Mockery must serialize and unserialize objects to avoid some ``__construct()`` insanity and attempting to mock a ``__wakeup()`` method as normal leads to a ``BadMethodCallException`` being thrown. 2. Mockery has two scenarios where real classes are replaced: Instance mocks and alias mocks. Both will generate PHP fatal errors if the real class is loaded, usually via a require or include statement. Only use these two mock types where autoloading is in place and where classes are not explicitly loaded on a per-file basis using ``require()``, ``require_once()``, etc. 3. Internal PHP classes are not entirely capable of being fully analysed using ``Reflection``. For example, ``Reflection`` cannot reveal details of expected parameters to the methods of such internal classes. As a result, there will be problems where a method parameter is defined to accept a value by reference (Mockery cannot detect this condition and will assume a pass by value on scalars and arrays). If references as internal class method parameters are needed, you should use the ``\Mockery\Configuration::setInternalClassMethodParamMap()`` method. Note, however that internal class parameter overriding is not available in PHP 8 since incompatible signatures have been reclassified as fatal errors. 4. Creating a mock implementing a certain interface with incorrect case in the interface name, and then creating a second mock implementing the same interface, but this time with the correct case, will have undefined behavior due to PHP's ``class_exists`` and related functions being case insensitive. Using the ``::class`` keyword in PHP can help you avoid these mistakes. The gotchas noted above are largely down to PHP's architecture and are assumed to be unavoidable. But - if you figure out a solution (or a better one than what may exist), let us know! PKGiZ<8kV)V) README.mdnuW+AMockery ======= [![Build Status](https://github.com/mockery/mockery/actions/workflows/tests.yml/badge.svg)](https://github.com/mockery/mockery/actions) [![Supported PHP Version](https://badgen.net/packagist/php/mockery/mockery?color=8892bf)](https://www.php.net/supported-versions) [![Code Coverage](https://codecov.io/gh/mockery/mockery/branch/1.6.x/graph/badge.svg?token=oxHwVM56bT)](https://codecov.io/gh/mockery/mockery) [![Type Coverage](https://shepherd.dev/github/mockery/mockery/coverage.svg)](https://shepherd.dev/github/mockery/mockery) [![Latest Stable Version](https://poser.pugx.org/mockery/mockery/v/stable.svg)](https://packagist.org/packages/mockery/mockery) [![Total Downloads](https://poser.pugx.org/mockery/mockery/downloads.svg)](https://packagist.org/packages/mockery/mockery) Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending. Mockery is released under a New BSD License. ## Installation To install Mockery, run the command below and you will get the latest version ```sh composer require --dev mockery/mockery ``` ## Documentation In older versions, this README file was the documentation for Mockery. Over time we have improved this, and have created an extensive documentation for you. Please use this README file as a starting point for Mockery, but do read the documentation to learn how to use Mockery. The current version can be seen at [docs.mockery.io](http://docs.mockery.io). ## PHPUnit Integration Mockery ships with some helpers if you are using PHPUnit. You can extend the [`Mockery\Adapter\Phpunit\MockeryTestCase`](library/Mockery/Adapter/Phpunit/MockeryTestCase.php) class instead of `PHPUnit\Framework\TestCase`, or if you are already using a custom base class for your tests, take a look at the traits available in the [`Mockery\Adapter\Phpunit`](library/Mockery/Adapter/Phpunit) namespace. ## Test Doubles Test doubles (often called mocks) simulate the behaviour of real objects. They are commonly utilised to offer test isolation, to stand in for objects which do not yet exist, or to allow for the exploratory design of class APIs without requiring actual implementation up front. The benefits of a test double framework are to allow for the flexible generation and configuration of test doubles. They allow the setting of expected method calls and/or return values using a flexible API which is capable of capturing every possible real object behaviour in way that is stated as close as possible to a natural language description. Use the `Mockery::mock` method to create a test double. ``` php $double = Mockery::mock(); ``` If you need Mockery to create a test double to satisfy a particular type hint, you can pass the type to the `mock` method. ``` php class Book {} interface BookRepository { function find($id): Book; function findAll(): array; function add(Book $book): void; } $double = Mockery::mock(BookRepository::class); ``` A detailed explanation of creating and working with test doubles is given in the documentation, [Creating test doubles](http://docs.mockery.io/en/latest/reference/creating_test_doubles.html) section. ## Method Stubs 🎫 A method stub is a mechanism for having your test double return canned responses to certain method calls. With stubs, you don't care how many times, if at all, the method is called. Stubs are used to provide indirect input to the system under test. ``` php $double->allows()->find(123)->andReturns(new Book()); $book = $double->find(123); ``` If you have used Mockery before, you might see something new in the example above — we created a method stub using `allows`, instead of the "old" `shouldReceive` syntax. This is a new feature of Mockery v1, but fear not, the trusty ol' `shouldReceive` is still here. For new users of Mockery, the above example can also be written as: ``` php $double->shouldReceive('find')->with(123)->andReturn(new Book()); $book = $double->find(123); ``` If your stub doesn't require specific arguments, you can also use this shortcut for setting up multiple calls at once: ``` php $double->allows([ "findAll" => [new Book(), new Book()], ]); ``` or ``` php $double->shouldReceive('findAll') ->andReturn([new Book(), new Book()]); ``` You can also use this shortcut, which creates a double and sets up some stubs in one call: ``` php $double = Mockery::mock(BookRepository::class, [ "findAll" => [new Book(), new Book()], ]); ``` ## Method Call Expectations 📲 A Method call expectation is a mechanism to allow you to verify that a particular method has been called. You can specify the parameters and you can also specify how many times you expect it to be called. Method call expectations are used to verify indirect output of the system under test. ``` php $book = new Book(); $double = Mockery::mock(BookRepository::class); $double->expects()->add($book); ``` During the test, Mockery accept calls to the `add` method as prescribed. After you have finished exercising the system under test, you need to tell Mockery to check that the method was called as expected, using the `Mockery::close` method. One way to do that is to add it to your `tearDown` method in PHPUnit. ``` php public function tearDown() { Mockery::close(); } ``` The `expects()` method automatically sets up an expectation that the method call (and matching parameters) is called **once and once only**. You can choose to change this if you are expecting more calls. ``` php $double->expects()->add($book)->twice(); ``` If you have used Mockery before, you might see something new in the example above — we created a method expectation using `expects`, instead of the "old" `shouldReceive` syntax. This is a new feature of Mockery v1, but same as with `allows` in the previous section, it can be written in the "old" style. For new users of Mockery, the above example can also be written as: ``` php $double->shouldReceive('find') ->with(123) ->once() ->andReturn(new Book()); $book = $double->find(123); ``` A detailed explanation of declaring expectations on method calls, please read the documentation, the [Expectation declarations](http://docs.mockery.io/en/latest/reference/expectations.html) section. After that, you can also learn about the new `allows` and `expects` methods in the [Alternative shouldReceive syntax](http://docs.mockery.io/en/latest/reference/alternative_should_receive_syntax.html) section. It is worth mentioning that one way of setting up expectations is no better or worse than the other. Under the hood, `allows` and `expects` are doing the same thing as `shouldReceive`, at times in "less words", and as such it comes to a personal preference of the programmer which way to use. ## Test Spies 🕵️ By default, all test doubles created with the `Mockery::mock` method will only accept calls that they have been configured to `allow` or `expect` (or in other words, calls that they `shouldReceive`). Sometimes we don't necessarily care about all of the calls that are going to be made to an object. To facilitate this, we can tell Mockery to ignore any calls it has not been told to expect or allow. To do so, we can tell a test double `shouldIgnoreMissing`, or we can create the double using the `Mocker::spy` shortcut. ``` php // $double = Mockery::mock()->shouldIgnoreMissing(); $double = Mockery::spy(); $double->foo(); // null $double->bar(); // null ``` Further to this, sometimes we want to have the object accept any call during the test execution and then verify the calls afterwards. For these purposes, we need our test double to act as a Spy. All mockery test doubles record the calls that are made to them for verification afterwards by default: ``` php $double->baz(123); $double->shouldHaveReceived()->baz(123); // null $double->shouldHaveReceived()->baz(12345); // Uncaught Exception Mockery\Exception\InvalidCountException... ``` Please refer to the [Spies](http://docs.mockery.io/en/latest/reference/spies.html) section of the documentation to learn more about the spies. ## Utilities 🔌 ### Global Helpers Mockery ships with a handful of global helper methods, you just need to ask Mockery to declare them. ``` php Mockery::globalHelpers(); $mock = mock(Some::class); $spy = spy(Some::class); $spy->shouldHaveReceived() ->foo(anyArgs()); ``` All of the global helpers are wrapped in a `!function_exists` call to avoid conflicts. So if you already have a global function called `spy`, Mockery will silently skip the declaring its own `spy` function. ### Testing Traits As Mockery ships with code generation capabilities, it was trivial to add functionality allowing users to create objects on the fly that use particular traits. Any abstract methods defined by the trait will be created and can have expectations or stubs configured like normal Test Doubles. ``` php trait Foo { function foo() { return $this->doFoo(); } abstract function doFoo(); } $double = Mockery::mock(Foo::class); $double->allows()->doFoo()->andReturns(123); $double->foo(); // int(123) ``` ## Versioning The Mockery team attempts to adhere to [Semantic Versioning](http://semver.org), however, some of Mockery's internals are considered private and will be open to change at any time. Just because a class isn't final, or a method isn't marked private, does not mean it constitutes part of the API we guarantee under the versioning scheme. ### Alternative Runtimes Mockery 1.3 was the last version to support HHVM 3 and PHP 5. There is no support for HHVM 4+. ## A new home for Mockery ⚠️️ Update your remotes! Mockery has transferred to a new location. While it was once at `padraic/mockery`, it is now at `mockery/mockery`. While your existing repositories will redirect transparently for any operations, take some time to transition to the new URL. ```sh $ git remote set-url upstream https://github.com/mockery/mockery.git ``` Replace `upstream` with the name of the remote you use locally; `upstream` is commonly used but you may be using something else. Run `git remote -v` to see what you're actually using. PKGiZd!͸ COPYRIGHT.mdnuW+A# Copyright - Copyright (c) [2009](https://github.com/mockery/mockery/commit/1d96f88142abe804ab9e893a5f07933f63e9bff9), [Pádraic Brady](https://github.com/padraic) - Copyright (c) [2011](https://github.com/mockery/mockery/commit/94dbb63aab37c659f63ea6e34acc6958928b0f59), [Robert Basic](https://github.com/robertbasic) - Copyright (c) [2012](https://github.com/mockery/mockery/commit/64e3ad6960eb3202b5b91b91a4ef1cf6252f0fef), [Dave Marshall](https://github.com/davedevelopment) - Copyright (c) [2013](https://github.com/mockery/mockery/commit/270ddd0bd051251e36a5688c52fc2638a097b110), [Graham Campbell](https://github.com/GrahamCampbell) - Copyright (c) [2017](https://github.com/mockery/mockery/commit/ba28b84c416b95924886bbd64a6a2f68e863536a), [Nathanael Esayeas](https://github.com/ghostwriter) PKGiZ^V'= SECURITY.mdnuW+A# Security Policy ## Supported Versions | Version | Supported | | ------- | ------------------ | | `2.0.x` | `yes` | | `1.6.x` | `yes` | | `1.5.x` | `yes` | | `<1.5.x` | `no` | ## Reporting a Vulnerability To report a security vulnerability, please [`Open a draft security advisory`](https://github.com/mockery/mockery/security/advisories/new) so we can coordinate the fix and disclosure. PKGiZ<( composer.jsonnuW+A{ "name": "mockery/mockery", "description": "Mockery is a simple yet flexible PHP mock object framework", "license": "BSD-3-Clause", "type": "library", "keywords": [ "bdd", "library", "mock", "mock objects", "mockery", "stub", "tdd", "test", "test double", "testing" ], "authors": [ { "name": "Pádraic Brady", "email": "padraic.brady@gmail.com", "homepage": "https://github.com/padraic", "role": "Author" }, { "name": "Dave Marshall", "email": "dave.marshall@atstsolutions.co.uk", "homepage": "https://davedevelopment.co.uk", "role": "Developer" }, { "name": "Nathanael Esayeas", "email": "nathanael.esayeas@protonmail.com", "homepage": "https://github.com/ghostwriter", "role": "Lead Developer" } ], "homepage": "https://github.com/mockery/mockery", "support": { "issues": "https://github.com/mockery/mockery/issues", "source": "https://github.com/mockery/mockery", "docs": "https://docs.mockery.io/", "rss": "https://github.com/mockery/mockery/releases.atom", "security": "https://github.com/mockery/mockery/security/advisories" }, "require": { "php": ">=7.3", "lib-pcre": ">=7.0", "hamcrest/hamcrest-php": "^2.0.1" }, "require-dev": { "phpunit/phpunit": "^8.5 || ^9.6.17", "symplify/easy-coding-standard": "^12.1.14" }, "conflict": { "phpunit/phpunit": "<8.0" }, "autoload": { "psr-4": { "Mockery\\": "library/Mockery" }, "files": [ "library/helpers.php", "library/Mockery.php" ] }, "autoload-dev": { "psr-4": { "Fixture\\": "tests/Fixture/", "Mockery\\Tests\\Unit\\": "tests/Unit", "test\\": "tests/" }, "files": [ "fixtures/autoload.php", "vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest.php" ] }, "config": { "optimize-autoloader": true, "platform": { "php": "7.3.999" }, "preferred-install": "dist", "sort-packages": true }, "scripts": { "check": [ "@composer validate", "@ecs", "@test" ], "docs": "vendor/bin/phpdoc -d library -t docs/api", "ecs": [ "@ecs:fix", "@ecs:check" ], "ecs:check": "ecs check --clear-cache || true", "ecs:fix": "ecs check --clear-cache --fix", "phive": [ "tools/phive update --force-accept-unsigned", "tools/phive purge" ], "phpunit": "vendor/bin/phpunit --do-not-cache-result --colors=always", "phpunit:coverage": "@phpunit --coverage-clover=coverage.xml", "psalm": "tools/psalm --no-cache --show-info=true", "psalm:alter": "tools/psalm --no-cache --alter --allow-backwards-incompatible-changes=false --safe-types", "psalm:baseline": "@psalm --no-diff --set-baseline=psalm-baseline.xml", "psalm:dry-run": "@psalm:alter --issues=all --dry-run", "psalm:fix": "@psalm:alter --issues=UnnecessaryVarAnnotation,MissingPureAnnotation,MissingImmutableAnnotation", "psalm:security": "@psalm --no-diff --taint-analysis", "psalm:shepherd": "@psalm --no-diff --shepherd --stats --output-format=github", "test": [ "@phpunit --stop-on-defect", "@psalm", "@psalm:security", "@psalm:dry-run" ] } } PKGiZ(9^^.phpstorm.meta.phpnuW+A "@"])); override(\Mockery::spy(0), map(["" => "@"])); override(\Mockery::namedMock(0), map(["" => "@"])); override(\Mockery::instanceMock(0), map(["" => "@"])); override(\mock(0), map(["" => "@"])); override(\spy(0), map(["" => "@"])); override(\namedMock(0), map(["" => "@"]));PKQZsGmockery/codecov.ymlnuW+Acomment: layout: "diff, files" behavior: default require_changes: true # if true: only post the comment if coverage changes require_base: yes # [yes :: must have a base report to post] require_head: yes # [yes :: must have a head report to post] PKQZ%\ܓKKmockery/psalm.xml.distnuW+A PKQZ<%==mockery/psalm-baseline.xmlnuW+A $expectation $expectation \Mockery\Matcher\MustBe new \Mockery\Matcher\MustBe($expected) \Mockery::builtInTypes() is_null(self::$_config) is_null(self::$_generator) is_null(self::$_loader) $newMockName $mock $argument $method $n $nesting $object function ($method) use ($add) { function ($n) use ($mock) { function ($object, $nesting) { $fqn $fqn $fqn $reference $type declareClass declareInterface declareType registerFileForCleanUp setGenerator setLoader $expectations $fileName $formatter($object, $nesting) $fqn $fqn $n $nesting $object $formattedArguments $k $arg $argument $argument[$key] $cleanedProperties[$name] $expectations $expectations $fileName $formattedArguments[] $formatter $mock $mock $name $reference $shortName $v $value $value $value $formatter($object, $nesting) \Mockery\ExpectationInterface \Mockery\MockInterface|\Mockery\LegacyMockInterface \Mockery\MockInterface|\Mockery\LegacyMockInterface \Mockery\MockInterface|\Mockery\LegacyMockInterface \Mockery\MockInterface|\Mockery\LegacyMockInterface mockery_getExpectationsFor shouldIgnoreMissing shouldReceive $name]]> $expectations $expectations shouldIgnoreMissing()]]> \Mockery\Mock null andAnyOtherArgs andAnyOthers any anyOf capture contains ducktype globalHelpers hasKey hasValue instanceMock isBuiltInType mustBe not notAnyOf on pattern resetContainer setContainer setGenerator setLoader subset type \Mockery\MockInterface|\Mockery\LegacyMockInterface $parentMock !== null Mockery\Container require $tmpfname $parRefMethod $parRefMethodRetType addToAssertionCount $mockeryOpen addMockeryExpectationsToAssertionCount checkMockeryExceptions closeMockery mockeryAssertPostConditions purgeMockeryContainer startMockery $e dismissed purgeMockeryContainer startMockery mockeryTestSetUp mockeryTestTearDown TestListener TestListenerDefaultImplementation $trait endTest startTestSuite TestListener getFileName()))]]> Blacklist::class new BlackList() BaseTestRunner::STATUS_PASSED addFailure getTestResultObject endTest startTestSuite endTest startTestSuite $result !== null Blacklist::$blacklistedClassNames Blacklist::$blacklistedClassNames __toString $exp $expectation $first $first $first $first \Mockery\Expectation \Mockery\Expectation \Mockery\MockInterface|\Mockery\LegacyMockInterface int self getMock getMock getMock getOrderNumber getMock()]]> getOrderNumber()]]> getMock(), 'shouldNotReceive'), $args)]]> getMock(), 'shouldReceive'), $args)]]> mock shouldNotReceive shouldReceive $class $class $class $class $defaultFormatter $formatterCallback $method $_constantsMap $_reflectionCacheEnabled allowMockingMethodsUnnecessarily allowMockingNonExistentMethods disableReflectionCache enableReflectionCache getConstantsMap getDefaultMatcher getInternalClassMethodParamMaps getObjectFormatter reflectionCacheEnabled resetInternalClassMethodParamMaps setConstantsMap setDefaultMatcher setInternalClassMethodParamMap setObjectFormatter $class $class $class $class $method $method $parentClass $parentClass $parentClass $parentClass \Hamcrest_Matcher::class _internalClassParamMap[strtolower($class)][strtolower($method)]]]> _internalClassParamMap[strtolower($class)][strtolower($method)]]]> _defaultMatchers[$type]]]> _objectFormatters[$class]]]> _objectFormatters[$type]]]> _defaultMatchers[$type]]]> _objectFormatters[$type]]]> $classes[] $classes[] $parentClass $parentClass $type $type array|null _internalClassParamMap[strtolower($class)][strtolower($method)]]]> $classes $classes allowMockingMethodsUnnecessarily allowMockingNonExistentMethods disableReflectionCache enableReflectionCache getInternalClassMethodParamMap mockingMethodsUnnecessarilyAllowed resetInternalClassMethodParamMaps setConstantsMap setDefaultMatcher setInternalClassMethodParamMap setObjectFormatter (bool) $flag (bool) $flag \Hamcrest_Matcher $arg instanceof MockConfigurationBuilder is_object($arg) is_string($arg) $mocks[$index] new $internalMockName() $mock $config $constructorArgs $mockName $reference _getInstance checkForNamedMockClashes getGenerator getLoader instanceMock mockery_setGroup $arg $blocks $constructorArgs getClassName()]]> getClassName()]]> $mock mockery_thrownExceptions()]]> $mockName getConstantsMap()]]> getInternalClassMethodParamMaps()]]> $keys _groups[$group]]]> _mocks[$reference]]]> _namedMocks[$name]]]> _namedMocks[$name]]]> $blocks $config $count $def $hash $instance $mock $mock $mock $mock $mock $name \Mockery\Mock \Mockery\Mock int atLeast byDefault generate getClassName getClassName getClassName getClassName getHash getName getTargetObject isInstanceMock load mockery_getExpectationCount mockery_init mockery_teardown mockery_thrownExceptions mockery_verify once setActualOrder setExpectedOrder setMethodName shouldReceive shouldReceive mockery_getExpectationCount()]]> $mockName $count $mocks[$index] _mocks[$reference]]]> Mock instanceMock mockery_allocateOrder mockery_getCurrentOrder mockery_getGroups mockery_setGroup \Mockery\LegacyMockInterface|\Mockery\MockInterface int mockingNonExistentMethodsAllowed() && (!class_exists($type, true) && !interface_exists($type, true))]]> mockingNonExistentMethodsAllowed() && (!class_exists($type, true) && !interface_exists($type, true))]]> 0]]> is_array($arg) is_object($finalArg) $class bool setActualCount setExpectedCount setExpectedCountComparative setMethodName AtLeast bool setActualCount setExpectedCount setExpectedCountComparative setMethodName AtMost null null __construct isEligible validate bool $because setActualCount setExpectedCount setExpectedCountComparative setMethodName _expectation->getExceptionMessage()]]> Exact $dismissed dismiss dismissed dismiss dismissed InvalidArgumentException $comp $count $count $name $actual $expected $expectedComparative $method $mockObject getActualCount getExpectedCount getExpectedCountComparative getMethodName getMock getMockName setActualCount setExpectedCount setExpectedCountComparative setMethodName setMock mockery_getName $comp getActualCount getExpectedCount getExpectedCountComparative getMethodName getMockName setActualCount setExpectedCount setExpectedCountComparative setMethodName $count $count $name $actual $expected $method $mockObject getActualOrder getExpectedOrder getMethodName getMock getMockName setActualOrder setExpectedOrder setMethodName setMock mockery_getName getActualOrder getExpectedOrder getMethodName getMockName setActualOrder setExpectedOrder setMethodName $count $name $actual $method $mockObject getActualArguments getMethodName getMock getMockName setActualArguments setMethodName setMock mockery_getName getActualArguments getMethodName getMockName setActualArguments setMethodName !is_int($index) is_int($limit) is_null($group) $argsOrClosure mixed self new $exception($message, $code, $previous) _countValidatorClass($this, $limit)]]> __toString $args $args function (...$args) use ($index) { static function () use ($args) { $code $exception $expectedArg $message $return andReturnFalse andReturnTrue andThrows between getExceptionMessage getName isAndAnyOtherArgumentsMatcher $code $exception $message $values $values _expectedArgs, true)]]> $arg $expectedArg $groups $lastExpectedArgument $matcher $newValidators[] $result $result $result $return $validator $validator $validator $value $values clone $validator _closureQueue), $args)]]> _closureQueue), $args)]]> bool int self self isEligible mockery_allocateOrder mockery_allocateOrder mockery_getGroups mockery_setGroup new $matcher($expected) validate $result _expectedArgs, true)]]> $group $group null null null null andReturnArg andReturnFalse andReturnNull andReturnSelf andReturnTrue andReturnUndefined andReturnUsing andThrowExceptions andThrows andYield because between byDefault globally isCallCountConstrained isEligible ordered passthru set twice with withSomeOfArgs zeroOrMoreTimes $_returnValue mixed mixed self _expectedArgs]]> $argsOrClosure instanceof Closure mockery_validateOrder \Hamcrest_Matcher mockery_callSubjectMethod mockery_returnValueForMethod $args $args $name addExpectation makeExpectationDefault $exp $exp $exp $exp $expectation $expectation $expectation $expectation $last isCallCountConstrained isEligible matchArgs matchArgs setActualArguments setMethodName verify verify verifyCall null null null call getExpectationCount verify $_expectedOrder $expectations andReturns getOrderNumber $args $method $cache generate cache[$hash]]]> cache[$hash]]]> $definition $name __toString $interface $method $alias $alias $name $rfc $interface $method rfc->getAttributes()]]> rfc->getInterfaces()]]> rfc->getMethods()]]> $child $child $parent getName getNamespaceName getShortName implementsInterface inNamespace isAbstract isFinal getAttributes getInterfaces getMethods getNamespaceName getParentClass getShortName implementsInterface inNamespace isAbstract isFinal isInternal isInternal name]]> rfc->getNamespaceName()]]> rfc->getShortName()]]> rfc->implementsInterface($interface)]]> rfc->inNamespace()]]> rfc->isAbstract()]]> rfc->isFinal()]]> generate $args $method $method $method $method $className $instanceMock $interfaces $mockOriginalDestructor $name $object $target $targetClassName $targetInterface $targetTraitName $allMethods $blackListedMethods $constantsMap $instanceMock $mockOriginalDestructor $name $parameterOverrides $targetClass $targetClassName $targetInterfaceNames $targetInterfaces $targetObject $targetTraitNames $targetTraits $whiteListedMethods addTarget addTargetInterfaceName addTargetTraitName addTargets generateName getAllMethods getBlackListedMethods getConstantsMap getMethodsToMock getName getNamespaceName getParameterOverrides getShortName getTargetClass getTargetClassName getTargetInterfaces getTargetObject getTargetTraits getWhiteListedMethods isInstanceMock isMockOriginalDestructor rename requiresCallStaticTypeHintRemoval requiresCallTypeHintRemoval setTargetClassName setTargetObject $alias getMethods()]]> $className $className getName()]]> getName()]]> $methods $methods $methods $methods $names $target $target $target $targetInterface $targetInterface $targetTrait blackListedMethods]]> constantsMap]]> getBlackListedMethods()]]> getBlackListedMethods()]]> getBlackListedMethods()]]> getName()]]> getName()]]> getTargetObject()]]> getWhiteListedMethods()]]> getWhiteListedMethods()]]> getWhiteListedMethods()]]> parameterOverrides]]> targetClassName]]> targetClassName]]> targetInterfaceNames]]> targetInterfaces]]> targetTraitNames]]> targetTraits]]> whiteListedMethods]]> $interface $interface $interface $interface $methods[$key] $params[1] $params[1] $target[0] getBlackListedMethods()]]> getWhiteListedMethods()]]> $classes[] $names[] targetInterfaceNames[]]]> targetInterfaces[]]]> targetInterfaces[]]]> targetInterfaces[]]]> targetInterfaces[]]]> targetInterfaces[]]]> targetTraitNames[]]]> targetTraits[]]]> $methods[$key] $alias $class $className $classes $classes[] $interface $key $method $method $method $method $methods $methods[] $names[] $params $params $targetInterface $targetTrait $targets[] $targets[] $trait addPart build getMethods getMethods getName getName getName getName getName getName getName getName getName getParameters getParameters hasInternalAncestor implementsInterface isAbstract isAbstract isArray isArray isFinal $target getParameterOverrides $blackListedMethod $instanceMock $mockDestructor $name $target $targets $whiteListedMethod $blackListedMethods $constantsMap $instanceMock $mockOriginalDestructor $name $parameterOverrides $php7SemiReservedKeywords $targets $whiteListedMethods addBlackListedMethod addBlackListedMethods addTarget addTargets addWhiteListedMethod addWhiteListedMethods getMockConfiguration setBlackListedMethods setConstantsMap setInstanceMock setMockOriginalDestructor setName setParameterOverrides setWhiteListedMethods blackListedMethods]]> blackListedMethods]]> constantsMap]]> parameterOverrides]]> php7SemiReservedKeywords]]> targets]]> whiteListedMethods]]> blackListedMethods[]]]> targets[]]]> whiteListedMethods[]]]> $method $method $target addWhiteListedMethods setBlackListedMethods setWhiteListedMethods $code $code $config getClassName getCode getConfig getName getConfig $part $mockCounter $parts addPart build $part $parts parts[]]]> $part static::$mockCounter static::$mockCounter \class_exists($typeHint) ? DefinedTargetClass::factory($typeHint, false) : null \ReflectionClass|null $method $typeHint getClass getTypeHintAsString isArray $method function ($method) { $code apply $code getMethodsToMock()]]> getName $code apply $code $code $code apply $code $class getAttributes $code apply $code $namespace $className $namespace $className $code apply $code getName()]]> $target getName isFinal $code apply $code $code $constant getName()]]]> getName()]]]> getName()]]]> $cm $cm $constant $value $i $class $code $code appendToClass apply $class $class $code $code $lastBrace apply $code apply $code getName()]]> getName()]]> $i getName getName $code $code $code $code $code $code string renderTypeHint getTargetClass()]]> $interface $method getName()]]> getName()]]> getName()]]> $interface $method $name getName()]]> $name renderTypeHint($parameter)]]> $param $param getName isPassedByReference $class $code $code $config $config $method appendToClass apply renderMethodBody renderParams renderReturnType renderTypeHint $class $class getName()]]> getName()]]> getName()]]> $method getParameters()]]> getName()]]]> getName())][$method->getName()]]]> $param $overrides[$class_name] getName()]]]> getName())]]]> getName())][$method->getName()]]]> getName()]]]> getName()]]]> getName())][$method->getName()]]]> getName())][$method->getName()]]]> $class $class $code $defaultValue $method $overrides $overrides $paramDef getDeclaringClass getName getName getName getName getName getName getParameterOverrides getParameterOverrides getParameters getReturnType isInternal isProtected isPublic isStatic isStatic returnsReference $code getName()]]> $paramDef renderMethodBody($method, $config)]]> renderParams($method, $config)]]> renderReturnType($method)]]> $lastBrace $code apply apply $code $methods apply $code methods[$method->getName()]]]> methods[$method->getName()]]]> methods[$method->getName()]]]> methods[$method->getName()]]]> $method $target getMethods getName getName isFinal apply $code apply $code $target apply $class $code $code appendToClass apply $class $class $code $target hasInternalAncestor implementsInterface $code $lastBrace apply $trait $code apply $code getName()]]> $traits $traits getName addPass generate $namedConfig $className $code $namedConfig $pass apply addPass new static([ new CallTypeHintPass(), new MagicMethodTypeHintsPass(), new ClassPass(), new TraitPass(), new ClassNamePass(), new InstanceMockPass(), new InterfacePass(), new AvoidMethodClashPass(), new MethodDefinitionPass(), new RemoveUnserializeForInternalSerializableClassesPass(), new RemoveBuiltinMethodsThatAreFinalPass(), new RemoveDestructorPass(), new ConstantsPass(), new ClassAttributesPass(), ]) getAttributes getShortName hasInternalAncestor implementsInterface inNamespace isAbstract __toString $name $name getName name]]> name]]> $args $method $method $method $expectation \Mockery\Expectation withArgs withArgs($args)]]> mock->{$this->method}($method, $args)]]> getName(), $file, $line ); $error = new UnexpectedValueException($msg, 0, new \Exception($message, $code)); }]]> function () use ($serializedString) { $className instantiate $className $instance hasInternalAncestors $error $method $method $method mockery_setCurrentOrder mockery_setGroup shouldAllowMockingMethod @var array $args @var string $method @var string $method @var string $method byDefault mockery_allocateOrder mockery_findExpectation mockery_getCurrentOrder mockery_getExpectationCount mockery_getGroups mockery_getMockableProperties mockery_init mockery_setGroup mockery_teardown mockery_verify shouldAllowMockingMethod shouldAllowMockingProtectedMethods shouldDeferMissing shouldHaveBeenCalled shouldNotHaveBeenCalled shouldNotReceive Mock \Mockery\ExpectationDirector|null null|array|Closure null|array|Closure load getClassName()]]> getCode()]]> load load $path $path load getClassName()]]> getCode()]]> $path path]]> require $tmpfname RequireLoader __toString __toString __toString __toString _expected]]> __toString $closure $result $closure($actual) __toString $actual $exp $v __toString $method _expected]]> $method __toString $actual _expected]]> __toString $actual __toString IsEqual __toString IsSame __toString __toString $closure call_user_func_array($closure, $actual) __toString __toString $actual __toString __toString $exp __toString _expected]]> __toString $strict $v loose strict new static($expected, false) new static($expected, true) __toString _expected]]> _expected]]> bool $function($actual) function_exists($function) $args $method $args $method getArgs getMethod getArgs getMethod __toString $method function ($method) { function () { yield; } $method $method $method $method $method $method $method $method $name $name $_mockery_allowMockingProtectedMethods $_mockery_instanceMock $_mockery_receivedMethodCalls _mockery_constructorCalled _mockery_findExpectedMethodHandler _mockery_getReceivedMethodCalls _mockery_handleMethodCall _mockery_handleStaticMethodCall asUndefined hasMethodOverloadingInParentClass mockery_getExpectations mockery_getMethod mockery_isInstance mockery_setCurrentOrder mockery_setGroup _mockery_expectations[$method]]]> _mockery_expectations[$method]]]> _mockery_expectations[$method]]]> _mockery_expectations[$method]]]> _mockery_groups[$group]]]> $allowMockingProtectedMethods $count $director $director $director $director $exp $expectation $exps $handler $method $method $method $method $prototype $returnValue $rm $rm $rm \Mockery\ExpectationDirector|null \Mockery\ExpectationInterface|\Mockery\Expectation|ExpectsHigherOrderMessage \Mockery\ExpectationInterface|\Mockery\Expectation|\Mockery\HigherOrderMessage \Mockery\Expectation|null int byDefault call findExpectation getExpectationCount getExpectations getName getName getName getName getPrototype isAbstract isAbstract isPrivate isProtected isProtected isProtected isPublic isPublic never push setActualOrder setExpectedOrder setMethodName verify $count $method $method $method $method $method $method $method $count findExpectation($args)]]> $expectation __call('__toString', array())]]> _mockery_expectations[$method]]]> shouldReceive($something)->once()]]> _mockery_mockableMethods]]> string[] $methodNames $methodNames @var array $args @var string $method @var string $method @var string $method null null null __callStatic _mockery_constructorCalled asUndefined mockery_callSubjectMethod mockery_getExpectations mockery_thrownExceptions $_mockery_name \Mockery\ExpectationDirector|null getName() !== 'Stringable']]> _mockery_partial)]]> _mockery_partial)]]> shouldIgnoreMissing shouldIgnoreMissing once andReturn once allows expects $methodCalls push verify getArgs()]]> getArgs()]]> methodCalls[]]]> $methodCall getArgs getArgs getMethod push $declaringClass getDeclaringClass()]]> $declaringClass $typeHint, 'isPrimitive' => in_array($typeHint, ['array', 'bool', 'int', 'float', 'null', 'object', 'string']), ], ]]]> string|null string $typeHint $typeHint ]]> $declaringClass $typeHint $typeHint getName isBuiltin $param $param __toString __call $args $method $args $args $args $args $limit $maximum $method $method $minimum atLeast atMost between cloneApplyAndVerify cloneWithoutCountValidatorsApplyAndVerify once times twice verify with withAnyArgs withArgs withNoArgs atLeast atMost between once times twice with withAnyArgs withArgs withNoArgs clearCountValidators $args $args $args andAnyOtherArgs andAnyOthers anyArgs mock namedMock spy PKQZsOEEmockery/.php-cs-fixer.dist.phpnuW+Ain([ 'library', 'tests', ]); return (new PhpCsFixer\Config()) ->setRules(array( '@PSR2' => true, )) ->setUsingCache(true) ->setFinder($finder) ; } $finder = DefaultFinder::create()->in( [ 'library', 'tests', ]); return Config::create() ->level('psr2') ->setUsingCache(true) ->finder($finder); PK'ZwDU map.rst.incnuW+APK'Z :e index.rstnuW+APK'Z_o o exceptions.rstnuW+APK'Z#xUU- reserved_method_names.rstnuW+APK'Z&#yconfiguration.rstnuW+APK'ZY   gotchas.rstnuW+APKZrr*mockery/.readthedocs.ymlnuW+APKZس >-mockery/CONTRIBUTING.mdnuW+APKZ}<:mockery/LICENSEnuW+APKZqbYUU@mockery/CHANGELOG.mdnuW+APKZOGGdmockery/composer.locknuW+APKZUR/mockery/library/Mockery/ReceivedMethodCalls.phpnuW+APKZ)5mockery/library/Mockery/ExpectsHigherOrderMessage.phpnuW+APKZss mmockery/library/Mockery/Mock.phpnuW+APKZGM*mockery/library/Mockery/Matcher/IsSame.phpnuW+APKZ:bm*mockery/library/Mockery/Matcher/HasKey.phpnuW+APKZrFH'|!mockery/library/Mockery/Matcher/Any.phpnuW+APKZF.gg($mockery/library/Mockery/Matcher/Type.phpnuW+APKZL[>3*mockery/library/Mockery/Matcher/AndAnyOtherArgs.phpnuW+APKZЀ8.mockery/library/Mockery/Matcher/MultiArgumentClosure.phpnuW+APKZ )PP'P2mockery/library/Mockery/Matcher/Not.phpnuW+APKZgFb+5mockery/library/Mockery/Matcher/IsEqual.phpnuW+APKZcA,m9mockery/library/Mockery/Matcher/NotAnyOf.phpnuW+APKZ *=mockery/library/Mockery/Matcher/Subset.phpnuW+APKZCUU+Gmockery/library/Mockery/Matcher/Pattern.phpnuW+APKZhj>XX,Jmockery/library/Mockery/Matcher/Contains.phpnuW+APKZ#h,lPmockery/library/Mockery/Matcher/HasValue.phpnuW+APKZE<*Tmockery/library/Mockery/Matcher/NoArgs.phpnuW+APKZ_+++Wmockery/library/Mockery/Matcher/Closure.phpnuW+APKZ*8M*V[mockery/library/Mockery/Matcher/MustBe.phpnuW+APKZ;QKK4_mockery/library/Mockery/Matcher/MatcherInterface.phpnuW+APKZb~__,6cmockery/library/Mockery/Matcher/Ducktype.phpnuW+APKZ+gmockery/library/Mockery/Matcher/AnyArgs.phpnuW+APKZ-Ryy)jmockery/library/Mockery/Matcher/AnyOf.phpnuW+APKZP,3nmockery/library/Mockery/Matcher/MatcherAbstract.phpnuW+APKZRPee7rmockery/library/Mockery/Matcher/ArgumentListMatcher.phpnuW+APKZ`X% ;itmockery/library/Mockery/Exception/InvalidCountException.phpnuW+APKZ ?mockery/library/Mockery/Exception/MockeryExceptionInterface.phpnuW+APKZ9aa;ہmockery/library/Mockery/Exception/InvalidOrderException.phpnuW+APKZ6mockery/library/Mockery/Exception/RuntimeException.phpnuW+APKZ謙__<mockery/library/Mockery/Exception/BadMethodCallException.phpnuW+APKZ ߯>mockery/library/Mockery/Exception/InvalidArgumentException.phpnuW+APKZZ$VVDmockery/library/Mockery/Exception/NoMatchingExpectationException.phpnuW+APKZ^ yrr.fmockery/library/Mockery/HigherOrderMessage.phpnuW+APKZN)))6mockery/library/Mockery/Configuration.phpnuW+APKZS^Bmockery/library/Mockery/CountValidator/CountValidatorInterface.phpnuW+APKZ4^c2(mockery/library/Mockery/CountValidator/AtLeast.phpnuW+APKZT4mockery/library/Mockery/CountValidator/Exception.phpnuW+APKZ-0mockery/library/Mockery/CountValidator/Exact.phpnuW+APKZ{4c@@1+mockery/library/Mockery/CountValidator/AtMost.phpnuW+APKZ8Amockery/library/Mockery/CountValidator/CountValidatorAbstract.phpnuW+APKZQb)Ymockery/library/Mockery/MockInterface.phpnuW+APKZ&,-mockery/library/Mockery/Loader/EvalLoader.phpnuW+APKZ{(1)mockery/library/Mockery/Loader/Loader.phpnuW+APKZ, 0Rmockery/library/Mockery/Loader/RequireLoader.phpnuW+APKZXr``'mockery/library/Mockery/Expectation.phpnuW+APKZ.j<<0Vmockery/library/Mockery/VerificationDirector.phpnuW+APKZ:kiHiH%Nemockery/library/Mockery/Container.phpnuW+APKZ% mockery/library/Mockery/Exception.phpnuW+APKZG’*Fmockery/library/Mockery/ClosureWrapper.phpnuW+APKZ 6 02mockery/library/Mockery/CompositeExpectation.phpnuW+APKZ^}^^/lmockery/library/Mockery/LegacyMockInterface.phpnuW+APKZs:ZZ3)mockery/library/Mockery/VerificationExpectation.phpnuW+APKZ!q**/mockery/library/Mockery/ExpectationDirector.phpnuW+APKZbepp:omockery/library/Mockery/Generator/TargetClassInterface.phpnuW+APKZJ9 9 AImockery/library/Mockery/Generator/StringManipulationGenerator.phpnuW+APKZk>mockery/library/Mockery/Generator/MockConfigurationBuilder.phpnuW+APKZPJ}No mockery/library/Mockery/Generator/StringManipulation/Pass/CallTypeHintPass.phpnuW+APKZ4qz%mockery/library/Mockery/Generator/StringManipulation/Pass/RemoveUnserializeForInternalSerializableClassesPass.phpnuW+APKZZG,mockery/library/Mockery/Generator/StringManipulation/Pass/ClassPass.phpnuW+APKZ]Q1mockery/library/Mockery/Generator/StringManipulation/Pass/ClassAttributesPass.phpnuW+APKZ_ZƗ N56mockery/library/Mockery/Generator/StringManipulation/Pass/InstanceMockPass.phpnuW+APKZșbbVJAmockery/library/Mockery/Generator/StringManipulation/Pass/MagicMethodTypeHintsPass.phpnuW+APKZw@f,K2Vmockery/library/Mockery/Generator/StringManipulation/Pass/ClassNamePass.phpnuW+APKZv??bbZmockery/library/Mockery/Generator/StringManipulation/Pass/RemoveBuiltinMethodsThatAreFinalPass.phpnuW+APKZ2ުK3amockery/library/Mockery/Generator/StringManipulation/Pass/InterfacePass.phpnuW+APKZGXfmockery/library/Mockery/Generator/StringManipulation/Pass/TraitPass.phpnuW+APKZsoRjmockery/library/Mockery/Generator/StringManipulation/Pass/AvoidMethodClashPass.phpnuW+APKZQ--Kpmockery/library/Mockery/Generator/StringManipulation/Pass/ConstantsPass.phpnuW+APKZ>vRumockery/library/Mockery/Generator/StringManipulation/Pass/MethodDefinitionPass.phpnuW+APKZ^~RKmockery/library/Mockery/Generator/StringManipulation/Pass/RemoveDestructorPass.phpnuW+APKZ0aBmockery/library/Mockery/Generator/StringManipulation/Pass/Pass.phpnuW+APKZnr;6'mockery/library/Mockery/Generator/CachingGenerator.phpnuW+APKZ—8;mockery/library/Mockery/Generator/DefinedTargetClass.phpnuW+APKZ4/mmockery/library/Mockery/Generator/Generator.phpnuW+APKZ( /mockery/library/Mockery/Generator/Parameter.phpnuW+APKZ_GJJ,mockery/library/Mockery/Generator/Method.phpnuW+APKZo>a5mockery/library/Mockery/Generator/MockNameBuilder.phpnuW+APKZ~uKuK7mockery/library/Mockery/Generator/MockConfiguration.phpnuW+APKZ *4 mockery/library/Mockery/Generator/MockDefinition.phpnuW+APKZ cZ Z :mockery/library/Mockery/Generator/UndefinedTargetClass.phpnuW+APKZw]0mockery/library/Mockery/ExpectationInterface.phpnuW+APKZW##%mockery/library/Mockery/Reflector.phpnuW+APKZeA800%Cmockery/library/Mockery/Undefined.phpnuW+APKZN^^&Fmockery/library/Mockery/MethodCall.phpnuW+APKZ9SJmockery/library/Mockery/QuickDefinitionsConfiguration.phpnuW+APKZyA =Qmockery/library/Mockery/Adapter/Phpunit/TestListenerTrait.phpnuW+APKZR??E\mockery/library/Mockery/Adapter/Phpunit/MockeryPHPUnitIntegration.phpnuW+APKZRR;Yemockery/library/Mockery/Adapter/Phpunit/MockeryTestCase.phpnuW+APKZVJa8hmockery/library/Mockery/Adapter/Phpunit/TestListener.phpnuW+APKZ^ hh@5lmockery/library/Mockery/Adapter/Phpunit/MockeryTestCaseSetUp.phpnuW+APKZN*  Y omockery/library/Mockery/Adapter/Phpunit/MockeryPHPUnitIntegrationAssertPostConditions.phpnuW+APKZӺ^(qmockery/library/Mockery/Instantiator.phpnuW+APKZ| 1f1fmockery/library/Mockery.phpnuW+APKZ,imockery/library/helpers.phpnuW+APKZJ(""@mockery/docs/conf.pynuW+APKZmockery/docs/_static/.gitkeepnuW+APKZ)-M  mockery/docs/requirements.txtnuW+APKZJX?mockery/docs/.gitignorenuW+APKZY~~mockery/docs/MakefilenuW+APKZ;g*P1mockery/docs/cookbook/big_parent_class.rstnuW+APKZIi==428mockery/docs/cookbook/mocking_class_within_class.rstnuW+APKZ:!Imockery/docs/cookbook/map.rst.incnuW+APKZ=b07Kmockery/docs/cookbook/detecting_mock_objects.rstnuW+APKZrxx3!Mmockery/docs/cookbook/mocking_hard_dependencies.rstnuW+APKZ?8E $^mockery/docs/cookbook/mockery_on.rstnuW+APKZIw;kmockery/docs/cookbook/index.rstnuW+APKZ2hh5lmockery/docs/cookbook/not_calling_the_constructor.rstnuW+APKZ$wF.humockery/docs/cookbook/default_expectations.rstnuW+APKZ$DD)xmockery/docs/cookbook/class_constants.rstnuW+APKZ8n"Y3Xmockery/docs/reference/public_static_properties.rstnuW+APKZעVM M <xmockery/docs/reference/alternative_should_receive_syntax.rstnuW+APKZ4~8~801mockery/docs/reference/creating_test_doubles.rstnuW+APKZa%%+mockery/docs/reference/instance_mocking.rstnuW+APKZ2>&&"mockery/docs/reference/map.rst.incnuW+APKZA. .mockery/docs/reference/phpunit_integration.rstnuW+APKZbgg)Zmockery/docs/reference/demeter_chains.rstnuW+APKZ437(mockery/docs/reference/partial_mocks.rstnuW+APKZQ55,0mockery/docs/reference/public_properties.rstnuW+APKZyd mockery/docs/reference/index.rstnuW+APKZDҼJ>J>'mockery/docs/reference/expectations.rstnuW+APKZZLig7WFmockery/docs/reference/pass_by_reference_behaviours.rstnuW+APKZG,Wmockery/docs/reference/protected_methods.rstnuW+APKZ03!)).Zmockery/docs/reference/argument_validation.rstnuW+APKZϬmGG0mockery/docs/reference/final_methods_classes.rstnuW+APKZ4%$ mockery/docs/reference/spies.rstnuW+APKZp(fmockery/docs/reference/magic_methods.rstnuW+APKZ$mmockery/docs/index.rstnuW+APKZ>(ETTmmockery/docs/README.mdnuW+APKZul%(mockery/docs/getting_started/map.rst.incnuW+APKZ]b!-mockery/docs/getting_started/installation.rstnuW+APKZH,&mockery/docs/getting_started/index.rstnuW+APKZc+D library/Mockery/Matcher/AndAnyOtherArgs.phpnuW+APKGiZЀ09H library/Mockery/Matcher/MultiArgumentClosure.phpnuW+APKGiZ )PPL library/Mockery/Matcher/Not.phpnuW+APKGiZgFb#P library/Mockery/Matcher/IsEqual.phpnuW+APKGiZcA$S library/Mockery/Matcher/NotAnyOf.phpnuW+APKGiZ "W library/Mockery/Matcher/Subset.phpnuW+APKGiZCUU#a library/Mockery/Matcher/Pattern.phpnuW+APKGiZhj>XX$d library/Mockery/Matcher/Contains.phpnuW+APKGiZ#h$kj library/Mockery/Matcher/HasValue.phpnuW+APKGiZE<"n library/Mockery/Matcher/NoArgs.phpnuW+APKGiZ_++#q library/Mockery/Matcher/Closure.phpnuW+APKGiZ*8M"=u library/Mockery/Matcher/MustBe.phpnuW+APKGiZ;QKK,fy library/Mockery/Matcher/MatcherInterface.phpnuW+APKGiZb~__$ } library/Mockery/Matcher/Ducktype.phpnuW+APKGiZ# library/Mockery/Matcher/AnyArgs.phpnuW+APKGiZ-Ryy! library/Mockery/Matcher/AnyOf.phpnuW+APKGiZP,+e library/Mockery/Matcher/MatcherAbstract.phpnuW+APKGiZRPee/T library/Mockery/Matcher/ArgumentListMatcher.phpnuW+APKGiZ`X% 3 library/Mockery/Exception/InvalidCountException.phpnuW+APKGiZ 7j library/Mockery/Exception/MockeryExceptionInterface.phpnuW+APKGiZ9aa3z library/Mockery/Exception/InvalidOrderException.phpnuW+APKGiZ.> library/Mockery/Exception/RuntimeException.phpnuW+APKGiZ謙__4C library/Mockery/Exception/BadMethodCallException.phpnuW+APKGiZ ߯6 library/Mockery/Exception/InvalidArgumentException.phpnuW+APKGiZZ$VV< library/Mockery/Exception/NoMatchingExpectationException.phpnuW+APKGiZ^ yrr&ݳ library/Mockery/HigherOrderMessage.phpnuW+APKGiZN))! library/Mockery/Configuration.phpnuW+APKGiZS^: library/Mockery/CountValidator/CountValidatorInterface.phpnuW+APKGiZ4^c* library/Mockery/CountValidator/AtLeast.phpnuW+APKGiZT, library/Mockery/CountValidator/Exception.phpnuW+APKGiZ-(9 library/Mockery/CountValidator/Exact.phpnuW+APKGiZ{4c@@)r library/Mockery/CountValidator/AtMost.phpnuW+APKGiZ89 library/Mockery/CountValidator/CountValidatorAbstract.phpnuW+APKGiZQb! library/Mockery/MockInterface.phpnuW+APKGiZ&,% library/Mockery/Loader/EvalLoader.phpnuW+APKGiZ{(1! library/Mockery/Loader/Loader.phpnuW+APKGiZ, (q library/Mockery/Loader/RequireLoader.phpnuW+APKGiZXr`` library/Mockery/Expectation.phpnuW+APKGiZ.j<<(o library/Mockery/VerificationDirector.phpnuW+APKGiZ:kiHiHU~ library/Mockery/Container.phpnuW+APKGiZ library/Mockery/Exception.phpnuW+APKGiZG’"= library/Mockery/ClosureWrapper.phpnuW+APKGiZ 6 (! library/Mockery/CompositeExpectation.phpnuW+APKGiZ^}^^'S library/Mockery/LegacyMockInterface.phpnuW+APKGiZs:ZZ+ library/Mockery/VerificationExpectation.phpnuW+APKGiZ!q**' library/Mockery/ExpectationDirector.phpnuW+APKGiZbepp2> library/Mockery/Generator/TargetClassInterface.phpnuW+APKGiZJ9 9 9 library/Mockery/Generator/StringManipulationGenerator.phpnuW+APKGiZk6 library/Mockery/Generator/MockConfigurationBuilder.phpnuW+APKGiZPJ}F&9 library/Mockery/Generator/StringManipulation/Pass/CallTypeHintPass.phpnuW+APKGiZ4i)> library/Mockery/Generator/StringManipulation/Pass/RemoveUnserializeForInternalSerializableClassesPass.phpnuW+APKGiZZ?~E library/Mockery/Generator/StringManipulation/Pass/ClassPass.phpnuW+APKGiZ]IlJ library/Mockery/Generator/StringManipulation/Pass/ClassAttributesPass.phpnuW+APKGiZ_ZƗ FN library/Mockery/Generator/StringManipulation/Pass/InstanceMockPass.phpnuW+APKGiZșbbNY library/Mockery/Generator/StringManipulation/Pass/MagicMethodTypeHintsPass.phpnuW+APKGiZw@f,Cn library/Mockery/Generator/StringManipulation/Pass/ClassNamePass.phpnuW+APKGiZv??Zr library/Mockery/Generator/StringManipulation/Pass/RemoveBuiltinMethodsThatAreFinalPass.phpnuW+APKGiZ2ުCy library/Mockery/Generator/StringManipulation/Pass/InterfacePass.phpnuW+APKGiZ?~ library/Mockery/Generator/StringManipulation/Pass/TraitPass.phpnuW+APKGiZsoJ8 library/Mockery/Generator/StringManipulation/Pass/AvoidMethodClashPass.phpnuW+APKGiZQ--Cp library/Mockery/Generator/StringManipulation/Pass/ConstantsPass.phpnuW+APKGiZ>vJ library/Mockery/Generator/StringManipulation/Pass/MethodDefinitionPass.phpnuW+APKGiZ^~J library/Mockery/Generator/StringManipulation/Pass/RemoveDestructorPass.phpnuW+APKGiZ0a: library/Mockery/Generator/StringManipulation/Pass/Pass.phpnuW+APKGiZnr;.f library/Mockery/Generator/CachingGenerator.phpnuW+APKGiZ—0r library/Mockery/Generator/DefinedTargetClass.phpnuW+APKGiZ4' library/Mockery/Generator/Generator.phpnuW+APKGiZ( ' library/Mockery/Generator/Parameter.phpnuW+APKGiZ_GJJ$ library/Mockery/Generator/Method.phpnuW+APKGiZo>a- library/Mockery/Generator/MockNameBuilder.phpnuW+APKGiZ~uKuK/ library/Mockery/Generator/MockConfiguration.phpnuW+APKGiZ *,$ library/Mockery/Generator/MockDefinition.phpnuW+APKGiZ cZ Z 2) library/Mockery/Generator/UndefinedTargetClass.phpnuW+APKGiZw](3 library/Mockery/ExpectationInterface.phpnuW+APKGiZW##6 library/Mockery/Reflector.phpnuW+APKGiZeA800[ library/Mockery/Undefined.phpnuW+APKGiZN^^~^ library/Mockery/MethodCall.phpnuW+APKGiZ1*b library/Mockery/QuickDefinitionsConfiguration.phpnuW+APKGiZyA 5yi library/Mockery/Adapter/Phpunit/TestListenerTrait.phpnuW+APKGiZR??=lt library/Mockery/Adapter/Phpunit/MockeryPHPUnitIntegration.phpnuW+APKGiZRR3} library/Mockery/Adapter/Phpunit/MockeryTestCase.phpnuW+APKGiZVJa0 library/Mockery/Adapter/Phpunit/TestListener.phpnuW+APKGiZ^ hh8 library/Mockery/Adapter/Phpunit/MockeryTestCaseSetUp.phpnuW+APKGiZN*  Q library/Mockery/Adapter/Phpunit/MockeryPHPUnitIntegrationAssertPostConditions.phpnuW+APKGiZӺ^ @ library/Mockery/Instantiator.phpnuW+APKGiZ| 1f1f library/Mockery.phpnuW+APKGiZ,library/helpers.phpnuW+APKGiZJ("" docs/conf.pynuW+APKGiZ+docs/_static/.gitkeepnuW+APKGiZ)-M  `+docs/requirements.txtnuW+APKGiZJX-docs/.gitignorenuW+APKGiZY~~ -docs/MakefilenuW+APKGiZ;g"Hdocs/cookbook/big_parent_class.rstnuW+APKGiZIi==,Odocs/cookbook/mocking_class_within_class.rstnuW+APKGiZ:"adocs/cookbook/map.rst.incnuW+APKGiZ=b(~bdocs/cookbook/detecting_mock_objects.rstnuW+APKGiZrxx+`ddocs/cookbook/mocking_hard_dependencies.rstnuW+APKGiZ?8E 3vdocs/cookbook/mockery_on.rstnuW+APKGiZIwjdocs/cookbook/index.rstnuW+APKGiZ2hh-ƒdocs/cookbook/not_calling_the_constructor.rstnuW+APKGiZ$wF&docs/cookbook/default_expectations.rstnuW+APKGiZ$DD!ҏdocs/cookbook/class_constants.rstnuW+APKGiZ8n"Y+gdocs/reference/public_static_properties.rstnuW+APKGiZעVM M 4docs/reference/alternative_should_receive_syntax.rstnuW+APKGiZ4~8~8(0docs/reference/creating_test_doubles.rstnuW+APKGiZa%%#docs/reference/instance_mocking.rstnuW+APKGiZ2>&&~docs/reference/map.rst.incnuW+APKGiZA. &docs/reference/phpunit_integration.rstnuW+APKGiZbgg!9docs/reference/demeter_chains.rstnuW+APKGiZ437 docs/reference/partial_mocks.rstnuW+APKGiZQ55$docs/reference/public_properties.rstnuW+APKGiZyddocs/reference/index.rstnuW+APKGiZDҼJ>J>udocs/reference/expectations.rstnuW+APKGiZZLig/]docs/reference/pass_by_reference_behaviours.rstnuW+APKGiZG$Ondocs/reference/protected_methods.rstnuW+APKGiZ03!))&?qdocs/reference/argument_validation.rstnuW+APKGiZϬmGG(docs/reference/final_methods_classes.rstnuW+APKGiZ4%$-docs/reference/spies.rstnuW+APKGiZp docs/reference/magic_methods.rstnuW+APKGiZ$docs/index.rstnuW+APKGiZ>(ETTdocs/README.mdnuW+APKGiZul% ~docs/getting_started/map.rst.incnuW+APKGiZ]b!%ldocs/getting_started/installation.rstnuW+APKGiZH,cdocs/getting_started/index.rstnuW+APKGiZc