├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── Tests ├── Fixtures │ └── Dummy.php ├── FunctionalTest │ └── ProxyObject │ │ ├── ProxyBuilderTest.php │ │ └── ProxyGenerationTest.php └── UnitTest │ └── ProxyObject │ ├── GeneratorTest.php │ └── ProxyBuilderTest.php ├── bin └── .gitignore ├── bootstrap.php ├── composer.json ├── composer.lock ├── doc └── config │ ├── README │ └── phpdoc.example.ini ├── phpunit.xml.dist └── src └── lapistano └── ProxyObject ├── Generator.php ├── Generator ├── proxied_class.tpl ├── proxied_method.tpl └── proxied_property.tpl ├── GeneratorException.php └── ProxyBuilder.php /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .buildpath 3 | .project 4 | .settings/ 5 | .idea/ 6 | build/ 7 | phpdoc.dist.ini 8 | phpdoc.ini 9 | doc/html/ 10 | vendor 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | before_install: 4 | - curl -s http://getcomposer.org/installer | php -- --install-dir=./bin 5 | - php ./bin/composer.phar install --dev 6 | 7 | php: 8 | - 5.3 9 | - 5.4 10 | 11 | notifications: 12 | email: 13 | - travis@bastian-feder.de 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | =========== 2 | ProxyObject 3 | =========== 4 | Initiated by Thomas Weinert back in 2008 I picked up his work and completed, extended, and tested it. 5 | The outcome is this little library making it much easier to generate a proxy of your system under test (SUT). 6 | Another thought on this library was, that it should be very easy to use if you know the way to mock classes and methods 7 | in PHPUnit. Proxy-object has almost the same API, but does not change the behavior of the proxied class/method. 8 | The only purpose is to expose hidden (protected & private) methods and members. 9 | 10 | Current travis status: [![Build Status](https://secure.travis-ci.org/lapistano/proxy-object.png?branch=master)](http://travis-ci.org/lapistano/proxy-object) 11 | 12 | Installation 13 | ============ 14 | Thanks to the feedback of [beberlei](https://github.com/beberlei) the source is now PSR-0 compatible. 15 | There is no specific installation routine to be followed. Just clone or checkout the source into to your project 16 | and use it. 17 | In case you don't use a [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) compatible autoloader, you only have to add the `bootstrap.php` into your bootstrap or 18 | autoloader. 19 | 20 | Composer 21 | -------- 22 | Add the following lines to your `composer.json` file and update your project's composer installation. 23 | 24 | ```json 25 | { 26 | "require-dev": { 27 | "lapistano/proxy-object": "2.*" 28 | } 29 | } 30 | ``` 31 | 32 | This composer configuration will checkout the sources tagged as the 2nd release. In case your want the 'cutting eadge' version 33 | replace '2.*' by 'dev-master'. But be alarmed that this might be broken sometimes. 34 | 35 | **NOTE:** 36 | In case you do not know what this means the [composer project website](http://getcomposer.org) is a good place to start. 37 | 38 | 39 | Github 40 | ------ 41 | Thus I recommend the composer way to make proxy-object a dependency to your project. 42 | The sources are also available via github. Just clone it as you might be familiar with. 43 | 44 | ```bash 45 | $ git clone git://github.com/lapistano/proxy-object.git 46 | ``` 47 | 48 | Usecases 49 | ======== 50 | 51 | 1. Exposing invisible Methods 52 | ----------------------------- 53 | One of the main purpose of this library is to expose invisible (private or protected) methods to the SUT. 54 | To do so use just create a new ProxyBuilder object and pass the method to be exposed. 55 | 56 | ```php 57 | 58 | $proxy = new \lapistano\ProxyObject\ProxyBuilder('myClass'); 59 | 60 | // generate and configure proxied object 61 | $proxiedObject = $proxy 62 | ->setConstructorArgs(array('Argument1', 'Argument2')) 63 | ->setMethods(array('myMethod')) 64 | ->getProxy(); 65 | 66 | // invoke proxied method 67 | $proxiedObject->myMethod(); 68 | 69 | ``` 70 | 71 | 2. Exposing invisible Members 72 | ----------------------------- 73 | Another purpose of this library is to expose invisible members not reachable via a setter. This is to prevent you 74 | from writing setter methods just for the purpose of unit testing. 75 | Use the `setProperties()` method to archieve. 76 | 77 | ```php 78 | $proxy = new \lapistano\ProxyObject\ProxyBuilder('myClass'); 79 | 80 | // generate and configure proxied object 81 | $proxiedObject = $proxy 82 | ->setProperties(array('myMember')) 83 | ->getProxy(); 84 | 85 | // change content proxied member 86 | $proxiedObject->myMember = 'another value'; 87 | 88 | ``` 89 | 90 | Despite the fact that it is possible to expose private members by naming them in the setProperties array, generating a 91 | proxy object without the property declaration will only expose protected members. This is because I am not a big fan of 92 | exposing too much from a class. If someone thinks this should be changed, I would be more than happy to discuss this topic. 93 | 94 | 3. Creating a proxied object without calling the constructor 95 | ------------------------------------------------------------ 96 | Sometimes it is necessary to supress the invokation of the defined constructor. 97 | Therefore I followed the API of PHPunits MockBuilder and added the `disableOriginalConstructor()` method. 98 | 99 | ```php 100 | 101 | $proxy = new \lapistano\ProxyObject\ProxyBuilder('myClass'); 102 | 103 | // generate and configure proxied object 104 | $proxiedObject = $proxy 105 | ->disableOriginalConstructor() 106 | ->getProxy(); 107 | 108 | // change value of proxied member 109 | $proxiedObject->myMember = 'another value'; 110 | 111 | ``` 112 | 113 | Ease access to the proxy-object in your test suite 114 | =================================================== 115 | Since I am really lazy ;) and I really like convenience I extended the `PHPUnit_Framework_TestCase` class and 116 | added the following method. 117 | 118 | ```php 119 | 120 | /** 121 | * Provides a ProxyBuilder object. 122 | * 123 | * @param string $classname 124 | * @return lapistano\ProxyObject\ProxyBuilder 125 | */ 126 | protected function getProxyBuilder($classname) { 127 | return new \lapistano\ProxyObject\ProxyBuilder($classname); 128 | } 129 | 130 | ``` 131 | 132 | Every of your test cases should now extend your own extended test case class so you can create a new proxy builder 133 | by just calling `$this->getProxyBuilder('\\my\\namespace\\myclass');`. Used in one of the examples above it will look like this. 134 | 135 | ```php 136 | 137 | // generate and configure proxied object 138 | $proxiedObject = $this->getProxyBuilder('myClass') 139 | ->disableOriginalConstructor() 140 | ->getProxy(); 141 | 142 | // change value of proxied member 143 | $proxiedObject->myMember = 'another value'; 144 | 145 | ``` 146 | 147 | 148 | Documentation 149 | ============= 150 | Since there is a exhausting documentation of the API in the source code, I decided not to write a separate one. 151 | Use [phpDocumentor](http://phpdoc.org) to extract and generate your own documentation. 152 | I added a phpdoc.example.ini in the doc/config folder. Follow the instructions in the `doc/config/README` to setup 153 | the generation of the documentation. 154 | 155 | Limitations 156 | =========== 157 | As you might expect there are also some limitations this library has to deal with. This limitations are not introduced 158 | by this implementation, but are limitations which come from PHP. So it is not possible to expose methods marked as 159 | final or static. 160 | 161 | Future stuff 162 | ============ 163 | - Improve error messages (e.g. by telling why a method/member could not be exposed) 164 | -------------------------------------------------------------------------------- /Tests/Fixtures/Dummy.php: -------------------------------------------------------------------------------- 1 | 'Tux', 1 => 'Beastie', 2 => 'Gnu',); 8 | 9 | public function test() 10 | { 11 | return 'test'; 12 | } 13 | } 14 | 15 | class DummyNoProtectedMembers 16 | { 17 | public $arms = array('left' => 'left arm', 'right' => 'right arm'); 18 | 19 | protected function getArm($position) 20 | { 21 | return $this->arms[$position]; 22 | } 23 | } 24 | 25 | class Dummy 26 | { 27 | 28 | public $arms = array('left' => 'left arm', 'right' => 'right arm'); 29 | 30 | protected $nervs = array(); 31 | protected $mascotts = array(0 => 'Tux', 1 => 'Beastie', 2 => 'Gnu',); 32 | 33 | protected function getArm($position) 34 | { 35 | return $this->arms[$position]; 36 | } 37 | 38 | } 39 | 40 | class DummyAllPublic 41 | { 42 | public function test() 43 | { 44 | return test; 45 | } 46 | } 47 | 48 | 49 | final class finalDummy 50 | { 51 | public function test() 52 | { 53 | return; 54 | } 55 | } 56 | 57 | interface DummyInterface 58 | { 59 | public function test(); 60 | } 61 | 62 | class DummyWithConstructor 63 | { 64 | public function __construct($beastie, array $os, \lapistano\Tests\ProxyObject\DummyNS $dummy) 65 | { 66 | return; 67 | } 68 | } 69 | 70 | class DummyWithConstructorAndUninitiableTypeHint 71 | { 72 | protected $myProtected = 'tux'; 73 | 74 | public function __construct($beastie, array $os, \Countable $dummy) 75 | { 76 | return; 77 | } 78 | 79 | protected function getMembers($dummy) 80 | { 81 | return $dummy; 82 | } 83 | } 84 | 85 | class ExtendsDummy extends Dummy 86 | { 87 | 88 | } 89 | 90 | class DummyWithNoProxyableProtectedMethods 91 | { 92 | 93 | public function tuxPublic() 94 | { 95 | return; 96 | } 97 | 98 | protected static function tuxStatic() 99 | { 100 | return; 101 | } 102 | 103 | final protected function tuxFinal() 104 | { 105 | return; 106 | } 107 | } 108 | 109 | } 110 | 111 | namespace lapistano\Tests\ProxyObject { 112 | 113 | class DummyNS 114 | { 115 | 116 | public $arms = array('left' => 'left arm', 'right' => 'right arm'); 117 | 118 | protected $nervs = array(); 119 | protected $mascotts = array(0 => 'Tux', 1 => 'Beastie', 2 => 'Gnu',); 120 | 121 | private $myPrivate; 122 | 123 | public function __construct($foo = '', array $os = array()) 124 | { 125 | return; 126 | } 127 | 128 | public function getArms() 129 | { 130 | return array_values($this->arms); 131 | } 132 | 133 | protected function getArm($position, $foo = '') 134 | { 135 | return $this->arms[$position]; 136 | } 137 | 138 | protected function getArmNS(\stdClass $dom) 139 | { 140 | return $this->arms['left']; 141 | } 142 | 143 | protected function setArms(array $arms = array()) 144 | { 145 | $this->arms = $arms; 146 | } 147 | 148 | protected static function arms() 149 | { 150 | return; 151 | } 152 | 153 | protected final function armsFinal() 154 | { 155 | return; 156 | } 157 | } 158 | 159 | class DummyNSnoConstruct 160 | { 161 | public $arms = array('left' => 'left arm', 'right' => 'right arm'); 162 | 163 | protected $nervs = array(); 164 | 165 | private $myPrivate = array(); 166 | 167 | public function getArms() 168 | { 169 | return array_values($this->arms); 170 | } 171 | } 172 | 173 | class DummyNSwithStatic 174 | { 175 | public $arms = array('left' => 'left arm', 'right' => 'right arm'); 176 | 177 | protected static $myStatic = 'tux'; 178 | protected $myProtectedFloat = 1.234; 179 | protected $myProtected = 'beastie'; 180 | 181 | private $myPrivate = array(); 182 | 183 | protected function &getArm($position, $foo = '') 184 | { 185 | return $this->arms[$position]; 186 | } 187 | } 188 | 189 | class ExtendsDummyNS extends DummyNS 190 | { 191 | 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /Tests/FunctionalTest/ProxyObject/ProxyBuilderTest.php: -------------------------------------------------------------------------------- 1 | . 6 | * All rights reserved. 7 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License Version 2.0, January 2004 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * @copyright 2010-2011 Bastian Feder 21 | * @author Bastian Feder 22 | * @license http://www.apache.org/licenses/LICENSE-2.0 23 | * @link https://github.com/lapistano/proxy-object 24 | * @package Unittests 25 | * @subpackage ProxyObject 26 | */ 27 | 28 | namespace lapistano\Tests\FunctionalTest\ProxyObject; 29 | 30 | use lapistano\ProxyObject\ProxyBuilder; 31 | 32 | /** 33 | * Functional test suite for the ProxyBuilder 34 | * 35 | * @copyright 2010-2012 Bastian Feder 36 | * @author Bastian Feder 37 | * @license http://www.apache.org/licenses/LICENSE-2.0 38 | * @link https://github.com/lapistano/proxy-object 39 | */ 40 | class ProxyBuilderFunctionalTest extends \PHPUnit_Framework_TestCase 41 | { 42 | 43 | /*************************************************************************/ 44 | /* Fixtures 45 | /*************************************************************************/ 46 | 47 | /** 48 | * Provides an instance of the ProxyBuilder. 49 | * 50 | * @param string $className 51 | * 52 | * @return \lapistano\ProxyObject\ProxyBuilder 53 | */ 54 | public function getProxyBuilderObject($className = '\lapistano\Tests\ProxyObject\DummyNS') 55 | { 56 | $pb = new ProxyBuilder($className); 57 | 58 | return $pb->setMethods(array('getArm', 'getArmNS')); 59 | } 60 | 61 | /*************************************************************************/ 62 | /* Tests 63 | /*************************************************************************/ 64 | 65 | public function testGetProxy() 66 | { 67 | $this->assertInstanceOf( 68 | '\lapistano\Tests\ProxyObject\DummyNS', 69 | $this->getProxyBuilderObject('\lapistano\Tests\ProxyObject\DummyNS')->getProxy() 70 | ); 71 | } 72 | 73 | public function testGetProxyNoNamespace() 74 | { 75 | $proxy = new ProxyBuilder('Dummy'); 76 | $this->assertInstanceOf('Dummy', $proxy->getProxy()); 77 | } 78 | 79 | public function testGetProxyBuilderNamespaced() 80 | { 81 | $this->assertInstanceOf( 82 | '\lapistano\Tests\ProxyObject\DummyNS', 83 | $this->getProxyBuilderObject('\lapistano\Tests\ProxyObject\DummyNS')->getProxy() 84 | ); 85 | } 86 | 87 | public function testGetProxyWithConstructorArguments() 88 | { 89 | $actual = $this->getProxyBuilderObject() 90 | ->setConstructorArgs(array(array())) 91 | ->getProxy(); 92 | $this->assertInstanceOf('\lapistano\Tests\ProxyObject\DummyNS', $actual); 93 | } 94 | 95 | public function testGetProxyByReflection() 96 | { 97 | $actual = $this->getProxyBuilderObject() 98 | ->setProperties(array('myPrivate', 'nervs')) 99 | ->getProxy(); 100 | $this->assertInstanceOf('\lapistano\Tests\ProxyObject\DummyNS', $actual); 101 | } 102 | 103 | public function testGetProxyDisableOriginalConstructor() 104 | { 105 | $actual = $this->getProxyBuilderObject() 106 | ->disableOriginalConstructor() 107 | ->getProxy(); 108 | $this->assertInstanceOf('\lapistano\Tests\ProxyObject\DummyNS', $actual); 109 | } 110 | 111 | public function testExposeInheritedMember() 112 | { 113 | $proxy = new ProxyBuilder('\ExtendsDummy'); 114 | $actual = $proxy 115 | ->setProperties(array('mascotts')) 116 | ->getProxy(); 117 | 118 | $this->assertEquals(array('Tux', 'Beastie', 'Gnu'), $actual->mascotts); 119 | } 120 | 121 | public function testConstruct() 122 | { 123 | $proxyBuilder = new ProxyBuilder('myProxyObject'); 124 | $this->assertAttributeEquals('myProxyObject', 'className', $proxyBuilder); 125 | } 126 | 127 | public function testGetProxyMethod() 128 | { 129 | $proxy = $this->getProxyBuilderObject() 130 | ->setMethods(array('getArm')) 131 | ->getProxy(); 132 | 133 | $this->assertEquals('right arm', $proxy->getArm('right')); 134 | } 135 | 136 | public function testGetProxyProperty() 137 | { 138 | $proxy = $this->getProxyBuilderObject() 139 | ->setProperties(array('myPrivate')) 140 | ->getProxy(); 141 | 142 | $proxy->myPrivate = 'beastie'; 143 | $this->assertEquals('beastie', $proxy->myPrivate); 144 | } 145 | 146 | public function testGetProxyNamespacedMethod() 147 | { 148 | $proxy = $this->getProxyBuilderObject()->getProxy(); 149 | $this->assertEquals('left arm', $proxy->getArmNS(new \stdClass)); 150 | } 151 | 152 | public function testSetConstructorArgs() 153 | { 154 | $args = array('beastie', 'tux'); 155 | 156 | $actual = $this->getProxyBuilderObject() 157 | ->setConstructorArgs($args); 158 | 159 | $this->assertInstanceOf('\lapistano\ProxyObject\ProxyBuilder', $actual); 160 | $this->assertAttributeEquals($args, 'constructorArgs', $actual); 161 | } 162 | 163 | public function testSetProxyClassName() 164 | { 165 | $classname = 'CustomClassNameProxy'; 166 | $actual = $this->getProxyBuilderObject() 167 | ->setProxyClassName($classname); 168 | 169 | $this->assertInstanceOf('\lapistano\ProxyObject\ProxyBuilder', $actual); 170 | $this->assertAttributeEquals($classname, 'mockClassName', $actual); 171 | } 172 | 173 | public function testDisableOriginalConstructor() 174 | { 175 | $proxyBuilder = $this->getProxyBuilderObject('\lapistano\Tests\ProxyObject\DummyNS'); 176 | $this->assertAttributeSame(true, 'invokeOriginalConstructor', $proxyBuilder); 177 | $this->assertInstanceOf('\lapistano\ProxyObject\ProxyBuilder', $proxyBuilder->disableOriginalConstructor()); 178 | $this->assertAttributeSame(false, 'invokeOriginalConstructor', $proxyBuilder); 179 | } 180 | 181 | public function testDisableAutoload() 182 | { 183 | $proxyBuilder = $this->getProxyBuilderObject('\lapistano\Tests\ProxyObject\DummyNS'); 184 | $this->assertAttributeSame(true, 'autoload', $proxyBuilder); 185 | $this->assertInstanceOf('\lapistano\ProxyObject\ProxyBuilder', $proxyBuilder->disableAutoload()); 186 | $this->assertAttributeSame(false, 'autoload', $proxyBuilder); 187 | } 188 | 189 | public function testGetInstanceOf() 190 | { 191 | $proxyBuilder = new ProxyBuilder('\lapistano\ProxyObject\ProxyBuilder'); 192 | $proxy = $proxyBuilder 193 | ->disableOriginalConstructor() 194 | ->setMethods(array('getInstanceOf')) 195 | ->getProxy(); 196 | $this->assertInstanceOf('stdClass', $proxy->getInstanceOf('stdClass')); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /Tests/FunctionalTest/ProxyObject/ProxyGenerationTest.php: -------------------------------------------------------------------------------- 1 | . 6 | * All rights reserved. 7 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License Version 2.0, January 2004 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * @copyright 2010-2011 Bastian Feder 21 | * @author Bastian Feder 22 | * @license http://www.apache.org/licenses/LICENSE-2.0 23 | * @link https://github.com/lapistano/proxy-object 24 | * @package Unittests 25 | * @subpackage ProxyObject 26 | */ 27 | 28 | namespace lapistano\Tests\FunctionalTests\ProxyObject; 29 | 30 | use lapistano\ProxyObject\ProxyBuilder; 31 | 32 | /** 33 | * 34 | * 35 | * @copyright 2010-2011 Bastian Feder 36 | * @author Bastian Feder 37 | * @license http://www.apache.org/licenses/LICENSE-2.0 38 | * @link https://github.com/lapistano/proxy-object 39 | * @package Unittests 40 | * @subpackage ProxyObject 41 | */ 42 | class ProxyGenerationTest extends \PHPUnit_Framework_TestCase 43 | { 44 | 45 | public function testProxyMemberOfClassWithNoHiddenMethod() 46 | { 47 | $proxy = new ProxyBuilder('DummyNoProtectedMethods'); 48 | $proxyDummyNS = $proxy->setProperties(array('mascotts'))->getProxy(); 49 | $this->assertInternalType('array', $proxyDummyNS->mascotts); 50 | } 51 | 52 | public function testGetCompleteProxyFromClass() 53 | { 54 | $proxy = new ProxyBuilder('Dummy'); 55 | $proxyDummy = $proxy->setMethods(array('getArm'))->getProxy(); 56 | $this->assertEquals('left arm', $proxyDummy->getArm('left')); 57 | $this->assertEquals('right arm', $proxyDummy->getArm('right')); 58 | } 59 | 60 | public function testGetCompleteProxyFromNamespacedClass() 61 | { 62 | $proxy = new ProxyBuilder('\lapistano\Tests\ProxyObject\DummyNS'); 63 | $proxyDummyNS = $proxy->setMethods( 64 | array( 65 | 'getArm', 66 | 'getArmNS' 67 | ) 68 | )->getProxy(); 69 | $this->assertEquals('left arm', $proxyDummyNS->getArmNS(new \stdClass)); 70 | $this->assertEquals('left arm', $proxyDummyNS->getArm('left')); 71 | $this->assertEquals('right arm', $proxyDummyNS->getArm('right')); 72 | } 73 | 74 | public function testGetCompleteProxyFromNamespacedClassSelectedProperty() 75 | { 76 | $proxy = new ProxyBuilder('\lapistano\Tests\ProxyObject\DummyNS'); 77 | $proxyDummyNS = $proxy->setMethods( 78 | array( 79 | 'getArm', 80 | 'getArmNS' 81 | ) 82 | )->setProperties(array('myPrivate'))->getProxy(); 83 | 84 | $proxyDummyNS->myPrivate = 'beastie'; 85 | 86 | $this->assertEquals('left arm', $proxyDummyNS->getArmNS(new \stdClass)); 87 | $this->assertEquals('left arm', $proxyDummyNS->getArm('left')); 88 | $this->assertEquals('right arm', $proxyDummyNS->getArm('right')); 89 | $this->assertEquals('beastie', $proxyDummyNS->myPrivate); 90 | } 91 | 92 | public function testGetProxyOfSingleMethodFromNamespacedClass() 93 | { 94 | $proxy = new ProxyBuilder('\lapistano\Tests\ProxyObject\DummyNSwithStatic'); 95 | $proxyDummyNS = $proxy->setMethods(array('getArm'))->setProperties( 96 | array( 97 | 'myPrivate', 98 | 'myProtected', 99 | 'myProtectedFloat' 100 | ) 101 | )->getProxy(); 102 | $this->assertEquals('left arm', $proxyDummyNS->getArm('left')); 103 | $this->assertEquals('right arm', $proxyDummyNS->getArm('right')); 104 | } 105 | 106 | /** 107 | * @dataProvider getProxyExpectingGeneratorException 108 | * @expectedException \lapistano\ProxyObject\GeneratorException 109 | */ 110 | public function testGetProxyExpectingGeneratorException($class, $methods) 111 | { 112 | $proxy = new ProxyBuilder($class); 113 | $proxyDummyNS = $proxy->setMethods($methods)->getProxy(); 114 | } 115 | 116 | public function testProxyMemberOfClassWithNoHiddenMember() 117 | { 118 | $proxy = new ProxyBuilder('DummyNoProtectedMembers'); 119 | $proxyDummyNS = $proxy->setMethods(array('getArm'))->getProxy(); 120 | $this->assertEquals('left arm', $proxyDummyNS->getArm('left')); 121 | } 122 | 123 | /** 124 | * @expectedException \lapistano\ProxyObject\GeneratorException 125 | */ 126 | public function testGetProxyOfClassWithStaticProtectedMethod() 127 | { 128 | $proxy = new ProxyBuilder('\DummyWithNoProxyableProtectedMethods'); 129 | $proxiedClass = $proxy->getProxy(); 130 | } 131 | 132 | /*************************************************************************/ 133 | /* Dataprovider & callbacks 134 | /*************************************************************************/ 135 | 136 | public static function getProxyExpectingGeneratorException() 137 | { 138 | return array( 139 | 'final method' => array( 140 | '\lapistano\Tests\ProxyObject\DummyNS', 141 | array('armsFinal') 142 | ), 143 | 'no protected methods with unknown method' => array( 144 | '\DummyAllPublic', 145 | array('unknown method') 146 | ), 147 | ); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /Tests/UnitTest/ProxyObject/GeneratorTest.php: -------------------------------------------------------------------------------- 1 | . 6 | * All rights reserved. 7 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License Version 2.0, January 2004 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * @copyright 2010-2011 Bastian Feder 21 | * @author Bastian Feder 22 | * @license http://www.apache.org/licenses/LICENSE-2.0 23 | * @link https://github.com/lapistano/proxy-object 24 | * @package Unittests 25 | * @subpackage ProxyObject 26 | */ 27 | 28 | namespace lapistano\Tests\UnitTest\ProxyObject; 29 | 30 | use lapistano\ProxyObject\Generator; 31 | 32 | /** 33 | * 34 | * 35 | * @copyright 2010-2011 Bastian Feder 36 | * @author Bastian Feder 37 | * @license http://www.apache.org/licenses/LICENSE-2.0 38 | * @link https://github.com/lapistano/proxy-object 39 | */ 40 | class GeneratorTest extends \PHPUnit_Framework_TestCase 41 | { 42 | /** 43 | * @param string $data 44 | * 45 | * @return mixed|string 46 | */ 47 | protected function slag($data) 48 | { 49 | return str_replace( 50 | array( 51 | "\r", 52 | "\r\n", 53 | "\n", 54 | " " 55 | ), "", $data 56 | ); 57 | } 58 | 59 | public function testGenerateWithPredefinedProxyName() 60 | { 61 | $expected = array( 62 | 'code' => "class DummyProxy extends Dummy\n" 63 | . "{\n\n" . "public \$nervs = array();\n" 64 | . "public \$mascotts = array(0 => 'Tux', 1 => 'Beastie', 2 => 'Gnu', );\n\n\n" 65 | . "public function getArm(\$position)\n" 66 | . " {\n" 67 | . " return parent::getArm(\$position);\n" 68 | . " }\n\n\n}\n", 69 | 'proxyClassName' => 'DummyProxy', 70 | 'namespaceName' => '' 71 | ); 72 | 73 | $generator = new Generator(); 74 | $result = $generator::generate('Dummy', null, null, 'DummyProxy'); 75 | 76 | $this->assertArrayHasKey('code', $result); 77 | $this->assertArrayHasKey('proxyClassName', $result); 78 | $this->assertArrayHasKey('namespaceName', $result); 79 | 80 | $this->assertEquals('DummyProxy', $result['proxyClassName']); 81 | 82 | $this->assertEquals( 83 | $this->slag($expected['code']), $this->slag($result['code']) 84 | ); 85 | } 86 | 87 | public function testGenerate() 88 | { 89 | $generator = new Generator(); 90 | $proxy = $generator::generate('Dummy'); 91 | 92 | $this->assertRegExp('/^Proxy_Dummy_\w+/', $proxy['proxyClassName']); 93 | $this->assertEmpty($proxy['namespaceName']); 94 | } 95 | 96 | public function testGenerateProxyFromCache() 97 | { 98 | $generator = new Generator(); 99 | $proxy = $generator::generate('Dummy'); 100 | $actual = $generator::generate('Dummy'); 101 | 102 | $this->assertRegExp('/^Proxy_Dummy_\w+/', $actual['proxyClassName']); 103 | $this->assertEmpty($actual['namespaceName']); 104 | } 105 | 106 | public function testGetMethodCallParameters() 107 | { 108 | $class = new \ReflectionClass('\lapistano\Tests\ProxyObject\DummyNS'); 109 | $proxy = new GeneratorProxy(); 110 | $method = $class->getMethod('getArm'); 111 | 112 | $this->assertEquals('$position, $foo', $proxy::getMethodCallParameters($method)); 113 | } 114 | 115 | /** 116 | * @expectedException \lapistano\ProxyObject\GeneratorException 117 | */ 118 | public function testGenerateProxyExpectingPHPUnit_Framework_ExceptionUnableToProxyMethod() 119 | { 120 | $expected = array( 121 | "namespaceName" => "\lapistano\Tests\ProxyObject" 122 | ); 123 | 124 | $actual = GeneratorProxy::generateProxy( 125 | '\lapistano\Tests\ProxyObject\DummyNS', array('arm') 126 | ); 127 | } 128 | 129 | public function testGenerateProxyAllProtectedMethods() 130 | { 131 | $expected = array( 132 | "namespaceName" => "\lapistano\Tests\ProxyObject" 133 | ); 134 | 135 | $actual = GeneratorProxy::generateProxy('Dummy'); 136 | 137 | $this->assertRegExp('/^Proxy_Dummy_\w+/', $actual['proxyClassName']); 138 | } 139 | 140 | /** 141 | * @expectedException \lapistano\ProxyObject\GeneratorException 142 | */ 143 | public function testGenerateProxyExpectingPHPUnit_Framework_ExceptionUnableToProxyMethods() 144 | { 145 | $actual = GeneratorProxy::generateProxy('\lapistano\Tests\ProxyObject\DummyNS', array('armsFinal')); 146 | } 147 | 148 | /** 149 | * @expectedException \lapistano\ProxyObject\GeneratorException 150 | * @dataProvider generateProxyExpectingExceptionDataprovider 151 | */ 152 | public function testGenerateProxyExpectingPHPUnit_Framework_Exception($class, $method) 153 | { 154 | $actual = GeneratorProxy::generateProxy($class, array($method)); 155 | } 156 | 157 | /** 158 | * @dataProvider getArgumentDeclarationDataprovider 159 | */ 160 | public function testGetArgumentDeclaration($expected, $method) 161 | { 162 | $class = new \ReflectionClass('\lapistano\Tests\ProxyObject\DummyNS'); 163 | $proxy = new GeneratorProxy(); 164 | $method = $class->getMethod($method); 165 | 166 | $this->assertEquals($expected, $proxy::getArgumentDeclaration($method)); 167 | } 168 | 169 | /** 170 | * @dataProvider getProxiedPropertiesDataprovider 171 | */ 172 | public function testGetProxiedProperties($expected, $className) 173 | { 174 | $class = new \ReflectionClass($className); 175 | $proxy = new GeneratorProxy(); 176 | 177 | $this->assertEquals( 178 | $this->slag($expected), $this->slag($proxy::getProxiedProperties('DummyNS', $class)) 179 | ); 180 | } 181 | 182 | public function testGetProxiedPropertiesSelectedProperty() 183 | { 184 | $class = new \ReflectionClass('\lapistano\Tests\ProxyObject\DummyNS'); 185 | $proxy = new GeneratorProxy(); 186 | 187 | $this->assertEquals( 188 | $this->slag('public $myPrivate;' . "\n"), 189 | $this->slag($proxy::getProxiedProperties('DummyNS', $class, array('myPrivate'))) 190 | ); 191 | } 192 | 193 | public function testGetProxiedPropertiesSelectedStaticProperty() 194 | { 195 | $class = new \ReflectionClass('\lapistano\Tests\ProxyObject\DummyNSwithStatic'); 196 | $proxy = new GeneratorProxy(); 197 | 198 | $this->assertEquals( 199 | $this->slag('public static $myStatic = \'tux\';' . "\n"), 200 | $this->slag($proxy::getProxiedProperties('DummyNSwithStatic', $class, array('myStatic'))) 201 | ); 202 | } 203 | 204 | /** 205 | * @dataProvider getProxiedPropertiesExceptionDataprovider 206 | * @expectedException \lapistano\ProxyObject\GeneratorException 207 | */ 208 | public function testGetProxiedPropertiesExpectingPHPUnit_Framework_Exception($property) 209 | { 210 | $class = new \ReflectionClass('\lapistano\Tests\ProxyObject\DummyNSwithStatic'); 211 | $proxy = new GeneratorProxy(); 212 | $proxy::getProxiedProperties('DummyNS', $class, array($property)); 213 | } 214 | 215 | /** 216 | * @dataProvider arrayToStringDataprovider 217 | */ 218 | public function testArrayToString($expected, $array) 219 | { 220 | $this->assertEquals($expected, GeneratorProxy::arrayToString($array)); 221 | } 222 | 223 | /** 224 | * @dataProvider canProxyMethodDataprovider 225 | */ 226 | public function testCanProxyMethod($method) 227 | { 228 | $reflected = new \ReflectionMethod('\lapistano\Tests\ProxyObject\DummyNS', $method); 229 | $this->assertFalse(GeneratorProxy::canProxyMethod($reflected)); 230 | } 231 | 232 | public function testCanProxyMethods() 233 | { 234 | $class = new \ReflectionClass('\lapistano\Tests\ProxyObject\DummyNS'); 235 | $methods = $class->getMethods(\ReflectionMethod::IS_PROTECTED); 236 | 237 | $this->assertCount(3, GeneratorProxy::canProxyMethods($methods)); 238 | 239 | } 240 | 241 | 242 | /*************************************************************************/ 243 | /* Dataprovider 244 | /*************************************************************************/ 245 | 246 | public static function canProxyMethodDataprovider() 247 | { 248 | return array( 249 | 'final method' => array('armsFinal'), 250 | 'static method' => array('arms'), 251 | 'public method' => array('getArms'), 252 | ); 253 | } 254 | 255 | public static function arrayToStringDataprovider() 256 | { 257 | return array( 258 | 'one level' => array( 259 | "0 => 'Tux', 1 => 'Beastie', ", 260 | array( 261 | 'Tux', 262 | 'Beastie' 263 | ) 264 | ), 265 | 'two level' => array( 266 | "'mascotts' => array (0 => 'Tux', 1 => 'Beastie', ), 0 => 'Foo', ", 267 | array( 268 | 'mascotts' => array( 269 | 'Tux', 270 | 'Beastie' 271 | ), 272 | 'Foo' 273 | ) 274 | ), 275 | 'mixed level' => array( 276 | "'mascotts' => array (0 => 'Tux', 1 => 'Beastie', ), 0 => array (0 => 'Foo', ), ", 277 | array( 278 | 'mascotts' => array( 279 | 'Tux', 280 | 'Beastie' 281 | ), 282 | array('Foo') 283 | ) 284 | ), 285 | ); 286 | } 287 | 288 | public static function getProxiedPropertiesExceptionDataprovider() 289 | { 290 | return array( 291 | 'Unknown Property' => array('Unknown Property'), 292 | ); 293 | } 294 | 295 | public static function getProxiedPropertiesDataprovider() 296 | { 297 | return array( 298 | 'with constructor' => array( 299 | "public \$nervs = array();\npublic \$mascotts = array(0 => 'Tux', 1 => 'Beastie', 2 => 'Gnu', );\npublic \$myPrivate;\n", 300 | '\lapistano\Tests\ProxyObject\DummyNS' 301 | ), 302 | 'without constructor' => array( 303 | "public \$nervs = array();\npublic \$myPrivate = array();\n", 304 | '\lapistano\Tests\ProxyObject\DummyNSnoConstruct' 305 | ), 306 | ); 307 | } 308 | 309 | public static function getArgumentDeclarationDataprovider() 310 | { 311 | return array( 312 | 'array' => array( 313 | "array \$arms = array (\n)", 314 | 'setArms' 315 | ), 316 | 'interface/class' => array( 317 | '\stdClass $dom', 318 | 'getArmNS' 319 | ), 320 | 'plain parameter' => array( 321 | '$position, $foo = \'\'', 322 | 'getArm' 323 | ), 324 | ); 325 | } 326 | 327 | public static function generateProxyExpectingExceptionDataprovider() 328 | { 329 | return array( 330 | 'not existing class' => array( 331 | 'NotExistingClass', 332 | 'getArm' 333 | ), 334 | 'final class' => array( 335 | 'finalDummy', 336 | 'getArm' 337 | ), 338 | 'interface' => array( 339 | 'DummyInterface', 340 | 'test' 341 | ), 342 | 'final method' => array( 343 | '\lapistano\Tests\ProxyObject\DummyNS', 344 | 'armsFinal' 345 | ), 346 | ); 347 | } 348 | } 349 | 350 | class GeneratorProxy extends \lapistano\ProxyObject\Generator 351 | { 352 | public static function getProxiedProperties($fullClassName, \ReflectionClass $class, array $properties = null) 353 | { 354 | return parent::getProxiedProperties($fullClassName, $class, $properties); 355 | } 356 | 357 | public static function canProxyMethod(\ReflectionMethod $method) 358 | { 359 | return parent::canProxyMethod($method); 360 | } 361 | 362 | public static function canProxyMethods(array $methods) 363 | { 364 | return parent::canProxyMethods($methods); 365 | } 366 | 367 | public function reflectMethods(array $methods, \ReflectionClass $class, $originalClassName) 368 | { 369 | return parent::reflectMethods($methods, $class, $originalClassName); 370 | } 371 | 372 | public static function generateProxy($originalClassName, array $methods = null, array $properties = null, 373 | $proxyClassName = '', $callAutoload = false) 374 | { 375 | return parent::generateProxy($originalClassName, $methods, $properties, $proxyClassName, $callAutoload); 376 | } 377 | 378 | public static function getArgumentDeclaration(\ReflectionMethod $method) 379 | { 380 | return parent::getArgumentDeclaration($method); 381 | } 382 | 383 | public static function getInstance(\ReflectionClass $class) 384 | { 385 | return parent::getInstance($class); 386 | } 387 | 388 | public static function arrayToString(array $array) 389 | { 390 | return parent::arrayToString($array); 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /Tests/UnitTest/ProxyObject/ProxyBuilderTest.php: -------------------------------------------------------------------------------- 1 | . 6 | * All rights reserved. 7 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License Version 2.0, January 2004 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * @copyright 2010-2011 Bastian Feder 21 | * @author Bastian Feder 22 | * @license http://www.apache.org/licenses/LICENSE-2.0 23 | * @link https://github.com/lapistano/proxy-object 24 | * @package Unittests 25 | * @subpackage ProxyObject 26 | */ 27 | 28 | namespace lapistano\Tests\UnitTest\ProxyObject; 29 | 30 | use lapistano\ProxyObject\ProxyBuilder; 31 | 32 | /** 33 | * 34 | * 35 | * @copyright 2010-2011 Bastian Feder 36 | * @author Bastian Feder 37 | * @license http://www.apache.org/licenses/LICENSE-2.0 38 | * @link https://github.com/lapistano/proxy-object 39 | */ 40 | class ProxyBuilderTest extends \PHPUnit_Framework_TestCase 41 | { 42 | 43 | /*************************************************************************/ 44 | /* Fixtures 45 | /*************************************************************************/ 46 | 47 | /** 48 | * Provides an instance of the ProxyBuilder. 49 | * 50 | * @param string $className 51 | * 52 | * @return \lapistano\ProxyObject\ProxyBuilder 53 | */ 54 | public function getProxyBuilderObject($className = '\lapistano\Tests\ProxyObject\DummyNS') 55 | { 56 | $pb = new ProxyBuilder($className); 57 | 58 | return $pb->setMethods( 59 | array( 60 | 'getArm', 61 | 'getArmNS' 62 | ) 63 | ); 64 | } 65 | 66 | /*************************************************************************/ 67 | /* Tests 68 | /*************************************************************************/ 69 | 70 | public function testGetProxy() 71 | { 72 | $this->assertInstanceOf( 73 | '\lapistano\Tests\ProxyObject\DummyNS', 74 | $this->getProxyBuilderObject('\lapistano\Tests\ProxyObject\DummyNS')->getProxy() 75 | ); 76 | } 77 | 78 | public function testGetProxyNoNamespace() 79 | { 80 | $proxy = new ProxyBuilder('Dummy'); 81 | $this->assertInstanceOf('Dummy', $proxy->getProxy()); 82 | } 83 | 84 | public function testGetProxyBuilderNamespaced() 85 | { 86 | $this->assertInstanceOf( 87 | '\lapistano\Tests\ProxyObject\DummyNS', 88 | $this->getProxyBuilderObject('\lapistano\Tests\ProxyObject\DummyNS')->getProxy() 89 | ); 90 | } 91 | 92 | public function testGetProxyWithConstructorArguments() 93 | { 94 | $actual = $this->getProxyBuilderObject()->setConstructorArgs(array(array()))->getProxy(); 95 | $this->assertInstanceOf('\lapistano\Tests\ProxyObject\DummyNS', $actual); 96 | } 97 | 98 | public function testGetProxyByReflection() 99 | { 100 | $actual = $this->getProxyBuilderObject()->setProperties( 101 | array( 102 | 'myPrivate', 103 | 'nervs' 104 | ) 105 | )->getProxy(); 106 | $this->assertInstanceOf('\lapistano\Tests\ProxyObject\DummyNS', $actual); 107 | } 108 | 109 | public function testGetProxyDisableOriginalConstructor() 110 | { 111 | $actual = $this->getProxyBuilderObject() 112 | ->disableOriginalConstructor() 113 | ->getProxy(); 114 | $this->assertInstanceOf('\lapistano\Tests\ProxyObject\DummyNS', $actual); 115 | } 116 | 117 | public function testExposeInheritedMember() 118 | { 119 | $proxy = new ProxyBuilder('\ExtendsDummy'); 120 | $actual = $proxy 121 | ->setProperties(array('mascotts')) 122 | ->getProxy(); 123 | 124 | $this->assertEquals( 125 | array( 126 | 'Tux', 127 | 'Beastie', 128 | 'Gnu' 129 | ), $actual->mascotts 130 | ); 131 | } 132 | 133 | public function testConstruct() 134 | { 135 | $proxyBuilder = new ProxyBuilder('myProxyObject'); 136 | $this->assertAttributeEquals('myProxyObject', 'className', $proxyBuilder); 137 | } 138 | 139 | public function testGetProxyMethod() 140 | { 141 | $proxy = $this->getProxyBuilderObject() 142 | ->setMethods(array('getArm')) 143 | ->getProxy(); 144 | 145 | $this->assertEquals('right arm', $proxy->getArm('right')); 146 | } 147 | 148 | public function testGetProxyProperty() 149 | { 150 | $proxy = $this->getProxyBuilderObject() 151 | ->setProperties(array('myPrivate')) 152 | ->getProxy(); 153 | 154 | $proxy->myPrivate = 'beastie'; 155 | $this->assertEquals('beastie', $proxy->myPrivate); 156 | } 157 | 158 | public function testGetProxyNamespacedMethod() 159 | { 160 | $proxy = $this 161 | ->getProxyBuilderObject() 162 | ->getProxy(); 163 | $this->assertEquals('left arm', $proxy->getArmNS(new \stdClass)); 164 | } 165 | 166 | public function testSetConstructorArgs() 167 | { 168 | $args = array( 169 | 'beastie', 170 | 'tux' 171 | ); 172 | 173 | $actual = $this->getProxyBuilderObject()->setConstructorArgs($args); 174 | 175 | $this->assertInstanceOf('\lapistano\ProxyObject\ProxyBuilder', $actual); 176 | $this->assertAttributeEquals($args, 'constructorArgs', $actual); 177 | } 178 | 179 | public function testSetProxyClassName() 180 | { 181 | $classname = 'CustomClassNameProxy'; 182 | $actual = $this->getProxyBuilderObject()->setProxyClassName($classname); 183 | 184 | $this->assertInstanceOf('\lapistano\ProxyObject\ProxyBuilder', $actual); 185 | $this->assertAttributeEquals($classname, 'mockClassName', $actual); 186 | } 187 | 188 | public function testDisableOriginalConstructor() 189 | { 190 | $proxyBuilder = $this->getProxyBuilderObject('\lapistano\Tests\ProxyObject\DummyNS'); 191 | 192 | $this->assertAttributeSame(true, 'invokeOriginalConstructor', $proxyBuilder); 193 | $this->assertInstanceOf('\lapistano\ProxyObject\ProxyBuilder', $proxyBuilder->disableOriginalConstructor()); 194 | $this->assertAttributeSame(false, 'invokeOriginalConstructor', $proxyBuilder); 195 | } 196 | 197 | public function testDisableAutoload() 198 | { 199 | $proxyBuilder = $this->getProxyBuilderObject('\lapistano\Tests\ProxyObject\DummyNS'); 200 | 201 | $this->assertAttributeSame(true, 'autoload', $proxyBuilder); 202 | $this->assertInstanceOf('\lapistano\ProxyObject\ProxyBuilder', $proxyBuilder->disableAutoload()); 203 | $this->assertAttributeSame(false, 'autoload', $proxyBuilder); 204 | } 205 | 206 | public function testGetInstanceOf() 207 | { 208 | $proxyBuilder = new ProxyBuilder('\lapistano\ProxyObject\ProxyBuilder'); 209 | $proxy = $proxyBuilder 210 | ->disableOriginalConstructor() 211 | ->setMethods(array('getInstanceOf')) 212 | ->getProxy(); 213 | 214 | $this->assertInstanceOf('stdClass', $proxy->getInstanceOf('stdClass')); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /bootstrap.php: -------------------------------------------------------------------------------- 1 | add($ns, $dir) to make a NS match to a dir not meeting PSR-0 11 | // $loader->addClassMap($dirList) to add a specific file or dir to the autolaoder. 12 | 13 | $loader = require_once __DIR__ . "/vendor/autoload.php"; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lapistano/proxy-object", 3 | "description": "A library that makes it much easier to generate a proxy of your system under test (SUT)", 4 | "keywords": ["testing", "proxy-object", "phpunit"], 5 | "homepage": "http://www.github.com/lapistano/proxy-object", 6 | "type": "library", 7 | "license": "Apache 2.0", 8 | "require": { 9 | "php":">=5.3.2" 10 | }, 11 | "autoload": { 12 | "psr-0": { "lapistano\\ProxyObject": "src/" } 13 | }, 14 | "authors": [ 15 | { 16 | "name": "Bastian Feder", 17 | "email": "lapis@bastian-feder.de", 18 | "homepage": "http://blog.bastian-feder.de" 19 | } 20 | ], 21 | "require-dev": { 22 | "phpunit/phpunit": "@stable" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "98f2f417ffc4c929eb036a2394605f64", 8 | "packages": [ 9 | 10 | ], 11 | "packages-dev": [ 12 | { 13 | "name": "phpunit/php-code-coverage", 14 | "version": "2.0.9", 15 | "source": { 16 | "type": "git", 17 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 18 | "reference": "ed8ac99ce38c3fd134128c898f7ca74665abef7f" 19 | }, 20 | "dist": { 21 | "type": "zip", 22 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ed8ac99ce38c3fd134128c898f7ca74665abef7f", 23 | "reference": "ed8ac99ce38c3fd134128c898f7ca74665abef7f", 24 | "shasum": "" 25 | }, 26 | "require": { 27 | "php": ">=5.3.3", 28 | "phpunit/php-file-iterator": "~1.3.1", 29 | "phpunit/php-text-template": "~1.2.0", 30 | "phpunit/php-token-stream": "~1.2.2", 31 | "sebastian/environment": "~1.0.0", 32 | "sebastian/version": "~1.0.3" 33 | }, 34 | "require-dev": { 35 | "ext-xdebug": ">=2.1.4", 36 | "phpunit/phpunit": "~4.0.14" 37 | }, 38 | "suggest": { 39 | "ext-dom": "*", 40 | "ext-xdebug": ">=2.2.1", 41 | "ext-xmlwriter": "*" 42 | }, 43 | "type": "library", 44 | "extra": { 45 | "branch-alias": { 46 | "dev-master": "2.0.x-dev" 47 | } 48 | }, 49 | "autoload": { 50 | "classmap": [ 51 | "src/" 52 | ] 53 | }, 54 | "notification-url": "https://packagist.org/downloads/", 55 | "include-path": [ 56 | "" 57 | ], 58 | "license": [ 59 | "BSD-3-Clause" 60 | ], 61 | "authors": [ 62 | { 63 | "name": "Sebastian Bergmann", 64 | "email": "sb@sebastian-bergmann.de", 65 | "role": "lead" 66 | } 67 | ], 68 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 69 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 70 | "keywords": [ 71 | "coverage", 72 | "testing", 73 | "xunit" 74 | ], 75 | "time": "2014-06-29 08:14:40" 76 | }, 77 | { 78 | "name": "phpunit/php-file-iterator", 79 | "version": "1.3.4", 80 | "source": { 81 | "type": "git", 82 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 83 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" 84 | }, 85 | "dist": { 86 | "type": "zip", 87 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", 88 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", 89 | "shasum": "" 90 | }, 91 | "require": { 92 | "php": ">=5.3.3" 93 | }, 94 | "type": "library", 95 | "autoload": { 96 | "classmap": [ 97 | "File/" 98 | ] 99 | }, 100 | "notification-url": "https://packagist.org/downloads/", 101 | "include-path": [ 102 | "" 103 | ], 104 | "license": [ 105 | "BSD-3-Clause" 106 | ], 107 | "authors": [ 108 | { 109 | "name": "Sebastian Bergmann", 110 | "email": "sb@sebastian-bergmann.de", 111 | "role": "lead" 112 | } 113 | ], 114 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 115 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 116 | "keywords": [ 117 | "filesystem", 118 | "iterator" 119 | ], 120 | "time": "2013-10-10 15:34:57" 121 | }, 122 | { 123 | "name": "phpunit/php-text-template", 124 | "version": "1.2.0", 125 | "source": { 126 | "type": "git", 127 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 128 | "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" 129 | }, 130 | "dist": { 131 | "type": "zip", 132 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", 133 | "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", 134 | "shasum": "" 135 | }, 136 | "require": { 137 | "php": ">=5.3.3" 138 | }, 139 | "type": "library", 140 | "autoload": { 141 | "classmap": [ 142 | "Text/" 143 | ] 144 | }, 145 | "notification-url": "https://packagist.org/downloads/", 146 | "include-path": [ 147 | "" 148 | ], 149 | "license": [ 150 | "BSD-3-Clause" 151 | ], 152 | "authors": [ 153 | { 154 | "name": "Sebastian Bergmann", 155 | "email": "sb@sebastian-bergmann.de", 156 | "role": "lead" 157 | } 158 | ], 159 | "description": "Simple template engine.", 160 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 161 | "keywords": [ 162 | "template" 163 | ], 164 | "time": "2014-01-30 17:20:04" 165 | }, 166 | { 167 | "name": "phpunit/php-timer", 168 | "version": "1.0.5", 169 | "source": { 170 | "type": "git", 171 | "url": "https://github.com/sebastianbergmann/php-timer.git", 172 | "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" 173 | }, 174 | "dist": { 175 | "type": "zip", 176 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", 177 | "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", 178 | "shasum": "" 179 | }, 180 | "require": { 181 | "php": ">=5.3.3" 182 | }, 183 | "type": "library", 184 | "autoload": { 185 | "classmap": [ 186 | "PHP/" 187 | ] 188 | }, 189 | "notification-url": "https://packagist.org/downloads/", 190 | "include-path": [ 191 | "" 192 | ], 193 | "license": [ 194 | "BSD-3-Clause" 195 | ], 196 | "authors": [ 197 | { 198 | "name": "Sebastian Bergmann", 199 | "email": "sb@sebastian-bergmann.de", 200 | "role": "lead" 201 | } 202 | ], 203 | "description": "Utility class for timing", 204 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 205 | "keywords": [ 206 | "timer" 207 | ], 208 | "time": "2013-08-02 07:42:54" 209 | }, 210 | { 211 | "name": "phpunit/php-token-stream", 212 | "version": "1.2.2", 213 | "source": { 214 | "type": "git", 215 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 216 | "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32" 217 | }, 218 | "dist": { 219 | "type": "zip", 220 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32", 221 | "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32", 222 | "shasum": "" 223 | }, 224 | "require": { 225 | "ext-tokenizer": "*", 226 | "php": ">=5.3.3" 227 | }, 228 | "type": "library", 229 | "extra": { 230 | "branch-alias": { 231 | "dev-master": "1.2-dev" 232 | } 233 | }, 234 | "autoload": { 235 | "classmap": [ 236 | "PHP/" 237 | ] 238 | }, 239 | "notification-url": "https://packagist.org/downloads/", 240 | "include-path": [ 241 | "" 242 | ], 243 | "license": [ 244 | "BSD-3-Clause" 245 | ], 246 | "authors": [ 247 | { 248 | "name": "Sebastian Bergmann", 249 | "email": "sb@sebastian-bergmann.de", 250 | "role": "lead" 251 | } 252 | ], 253 | "description": "Wrapper around PHP's tokenizer extension.", 254 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 255 | "keywords": [ 256 | "tokenizer" 257 | ], 258 | "time": "2014-03-03 05:10:30" 259 | }, 260 | { 261 | "name": "phpunit/phpunit", 262 | "version": "4.1.3", 263 | "source": { 264 | "type": "git", 265 | "url": "https://github.com/sebastianbergmann/phpunit.git", 266 | "reference": "939cb801b3b2aa253aedd0b279f40bb8f35cec91" 267 | }, 268 | "dist": { 269 | "type": "zip", 270 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/939cb801b3b2aa253aedd0b279f40bb8f35cec91", 271 | "reference": "939cb801b3b2aa253aedd0b279f40bb8f35cec91", 272 | "shasum": "" 273 | }, 274 | "require": { 275 | "ext-dom": "*", 276 | "ext-json": "*", 277 | "ext-pcre": "*", 278 | "ext-reflection": "*", 279 | "ext-spl": "*", 280 | "php": ">=5.3.3", 281 | "phpunit/php-code-coverage": "~2.0", 282 | "phpunit/php-file-iterator": "~1.3.1", 283 | "phpunit/php-text-template": "~1.2", 284 | "phpunit/php-timer": "~1.0.2", 285 | "phpunit/phpunit-mock-objects": "~2.1", 286 | "sebastian/comparator": "~1.0", 287 | "sebastian/diff": "~1.1", 288 | "sebastian/environment": "~1.0", 289 | "sebastian/exporter": "~1.0", 290 | "sebastian/version": "~1.0", 291 | "symfony/yaml": "~2.0" 292 | }, 293 | "suggest": { 294 | "phpunit/php-invoker": "~1.1" 295 | }, 296 | "bin": [ 297 | "phpunit" 298 | ], 299 | "type": "library", 300 | "extra": { 301 | "branch-alias": { 302 | "dev-master": "4.1.x-dev" 303 | } 304 | }, 305 | "autoload": { 306 | "classmap": [ 307 | "src/" 308 | ] 309 | }, 310 | "notification-url": "https://packagist.org/downloads/", 311 | "include-path": [ 312 | "", 313 | "../../symfony/yaml/" 314 | ], 315 | "license": [ 316 | "BSD-3-Clause" 317 | ], 318 | "authors": [ 319 | { 320 | "name": "Sebastian Bergmann", 321 | "email": "sebastian@phpunit.de", 322 | "role": "lead" 323 | } 324 | ], 325 | "description": "The PHP Unit Testing framework.", 326 | "homepage": "http://www.phpunit.de/", 327 | "keywords": [ 328 | "phpunit", 329 | "testing", 330 | "xunit" 331 | ], 332 | "time": "2014-06-11 14:15:47" 333 | }, 334 | { 335 | "name": "phpunit/phpunit-mock-objects", 336 | "version": "2.1.5", 337 | "source": { 338 | "type": "git", 339 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 340 | "reference": "7878b9c41edb3afab92b85edf5f0981014a2713a" 341 | }, 342 | "dist": { 343 | "type": "zip", 344 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/7878b9c41edb3afab92b85edf5f0981014a2713a", 345 | "reference": "7878b9c41edb3afab92b85edf5f0981014a2713a", 346 | "shasum": "" 347 | }, 348 | "require": { 349 | "php": ">=5.3.3", 350 | "phpunit/php-text-template": "~1.2" 351 | }, 352 | "require-dev": { 353 | "phpunit/phpunit": "~4.1" 354 | }, 355 | "suggest": { 356 | "ext-soap": "*" 357 | }, 358 | "type": "library", 359 | "extra": { 360 | "branch-alias": { 361 | "dev-master": "2.1.x-dev" 362 | } 363 | }, 364 | "autoload": { 365 | "classmap": [ 366 | "src/" 367 | ] 368 | }, 369 | "notification-url": "https://packagist.org/downloads/", 370 | "include-path": [ 371 | "" 372 | ], 373 | "license": [ 374 | "BSD-3-Clause" 375 | ], 376 | "authors": [ 377 | { 378 | "name": "Sebastian Bergmann", 379 | "email": "sb@sebastian-bergmann.de", 380 | "role": "lead" 381 | } 382 | ], 383 | "description": "Mock Object library for PHPUnit", 384 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 385 | "keywords": [ 386 | "mock", 387 | "xunit" 388 | ], 389 | "time": "2014-06-12 07:22:15" 390 | }, 391 | { 392 | "name": "sebastian/comparator", 393 | "version": "1.0.0", 394 | "source": { 395 | "type": "git", 396 | "url": "https://github.com/sebastianbergmann/comparator.git", 397 | "reference": "f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2" 398 | }, 399 | "dist": { 400 | "type": "zip", 401 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2", 402 | "reference": "f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2", 403 | "shasum": "" 404 | }, 405 | "require": { 406 | "php": ">=5.3.3", 407 | "sebastian/diff": "~1.1", 408 | "sebastian/exporter": "~1.0" 409 | }, 410 | "require-dev": { 411 | "phpunit/phpunit": "~4.1" 412 | }, 413 | "type": "library", 414 | "extra": { 415 | "branch-alias": { 416 | "dev-master": "1.0.x-dev" 417 | } 418 | }, 419 | "autoload": { 420 | "classmap": [ 421 | "src/" 422 | ] 423 | }, 424 | "notification-url": "https://packagist.org/downloads/", 425 | "license": [ 426 | "BSD-3-Clause" 427 | ], 428 | "authors": [ 429 | { 430 | "name": "Sebastian Bergmann", 431 | "email": "sebastian@phpunit.de", 432 | "role": "lead" 433 | }, 434 | { 435 | "name": "Jeff Welch", 436 | "email": "whatthejeff@gmail.com" 437 | }, 438 | { 439 | "name": "Volker Dusch", 440 | "email": "github@wallbash.com" 441 | }, 442 | { 443 | "name": "Bernhard Schussek", 444 | "email": "bschussek@2bepublished.at" 445 | } 446 | ], 447 | "description": "Provides the functionality to compare PHP values for equality", 448 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 449 | "keywords": [ 450 | "comparator", 451 | "compare", 452 | "equality" 453 | ], 454 | "time": "2014-05-02 07:05:58" 455 | }, 456 | { 457 | "name": "sebastian/diff", 458 | "version": "1.1.0", 459 | "source": { 460 | "type": "git", 461 | "url": "https://github.com/sebastianbergmann/diff.git", 462 | "reference": "1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d" 463 | }, 464 | "dist": { 465 | "type": "zip", 466 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d", 467 | "reference": "1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d", 468 | "shasum": "" 469 | }, 470 | "require": { 471 | "php": ">=5.3.3" 472 | }, 473 | "type": "library", 474 | "extra": { 475 | "branch-alias": { 476 | "dev-master": "1.1-dev" 477 | } 478 | }, 479 | "autoload": { 480 | "classmap": [ 481 | "src/" 482 | ] 483 | }, 484 | "notification-url": "https://packagist.org/downloads/", 485 | "license": [ 486 | "BSD-3-Clause" 487 | ], 488 | "authors": [ 489 | { 490 | "name": "Sebastian Bergmann", 491 | "email": "sebastian@phpunit.de", 492 | "role": "lead" 493 | }, 494 | { 495 | "name": "Kore Nordmann", 496 | "email": "mail@kore-nordmann.de" 497 | } 498 | ], 499 | "description": "Diff implementation", 500 | "homepage": "http://www.github.com/sebastianbergmann/diff", 501 | "keywords": [ 502 | "diff" 503 | ], 504 | "time": "2013-08-03 16:46:33" 505 | }, 506 | { 507 | "name": "sebastian/environment", 508 | "version": "1.0.0", 509 | "source": { 510 | "type": "git", 511 | "url": "https://github.com/sebastianbergmann/environment.git", 512 | "reference": "79517609ec01139cd7e9fded0dd7ce08c952ef6a" 513 | }, 514 | "dist": { 515 | "type": "zip", 516 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/79517609ec01139cd7e9fded0dd7ce08c952ef6a", 517 | "reference": "79517609ec01139cd7e9fded0dd7ce08c952ef6a", 518 | "shasum": "" 519 | }, 520 | "require": { 521 | "php": ">=5.3.3" 522 | }, 523 | "require-dev": { 524 | "phpunit/phpunit": "4.0.*@dev" 525 | }, 526 | "type": "library", 527 | "extra": { 528 | "branch-alias": { 529 | "dev-master": "1.0.x-dev" 530 | } 531 | }, 532 | "autoload": { 533 | "classmap": [ 534 | "src/" 535 | ] 536 | }, 537 | "notification-url": "https://packagist.org/downloads/", 538 | "license": [ 539 | "BSD-3-Clause" 540 | ], 541 | "authors": [ 542 | { 543 | "name": "Sebastian Bergmann", 544 | "email": "sebastian@phpunit.de", 545 | "role": "lead" 546 | } 547 | ], 548 | "description": "Provides functionality to handle HHVM/PHP environments", 549 | "homepage": "http://www.github.com/sebastianbergmann/environment", 550 | "keywords": [ 551 | "Xdebug", 552 | "environment", 553 | "hhvm" 554 | ], 555 | "time": "2014-02-18 16:17:19" 556 | }, 557 | { 558 | "name": "sebastian/exporter", 559 | "version": "1.0.1", 560 | "source": { 561 | "type": "git", 562 | "url": "https://github.com/sebastianbergmann/exporter.git", 563 | "reference": "1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529" 564 | }, 565 | "dist": { 566 | "type": "zip", 567 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529", 568 | "reference": "1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529", 569 | "shasum": "" 570 | }, 571 | "require": { 572 | "php": ">=5.3.3" 573 | }, 574 | "require-dev": { 575 | "phpunit/phpunit": "4.0.*@dev" 576 | }, 577 | "type": "library", 578 | "extra": { 579 | "branch-alias": { 580 | "dev-master": "1.0.x-dev" 581 | } 582 | }, 583 | "autoload": { 584 | "classmap": [ 585 | "src/" 586 | ] 587 | }, 588 | "notification-url": "https://packagist.org/downloads/", 589 | "license": [ 590 | "BSD-3-Clause" 591 | ], 592 | "authors": [ 593 | { 594 | "name": "Sebastian Bergmann", 595 | "email": "sebastian@phpunit.de", 596 | "role": "lead" 597 | }, 598 | { 599 | "name": "Jeff Welch", 600 | "email": "whatthejeff@gmail.com" 601 | }, 602 | { 603 | "name": "Volker Dusch", 604 | "email": "github@wallbash.com" 605 | }, 606 | { 607 | "name": "Adam Harvey", 608 | "email": "aharvey@php.net", 609 | "role": "Lead" 610 | }, 611 | { 612 | "name": "Bernhard Schussek", 613 | "email": "bschussek@2bepublished.at" 614 | } 615 | ], 616 | "description": "Provides the functionality to export PHP variables for visualization", 617 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 618 | "keywords": [ 619 | "export", 620 | "exporter" 621 | ], 622 | "time": "2014-02-16 08:26:31" 623 | }, 624 | { 625 | "name": "sebastian/version", 626 | "version": "1.0.3", 627 | "source": { 628 | "type": "git", 629 | "url": "https://github.com/sebastianbergmann/version.git", 630 | "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43" 631 | }, 632 | "dist": { 633 | "type": "zip", 634 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", 635 | "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", 636 | "shasum": "" 637 | }, 638 | "type": "library", 639 | "autoload": { 640 | "classmap": [ 641 | "src/" 642 | ] 643 | }, 644 | "notification-url": "https://packagist.org/downloads/", 645 | "license": [ 646 | "BSD-3-Clause" 647 | ], 648 | "authors": [ 649 | { 650 | "name": "Sebastian Bergmann", 651 | "email": "sebastian@phpunit.de", 652 | "role": "lead" 653 | } 654 | ], 655 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 656 | "homepage": "https://github.com/sebastianbergmann/version", 657 | "time": "2014-03-07 15:35:33" 658 | }, 659 | { 660 | "name": "symfony/yaml", 661 | "version": "v2.5.1", 662 | "target-dir": "Symfony/Component/Yaml", 663 | "source": { 664 | "type": "git", 665 | "url": "https://github.com/symfony/Yaml.git", 666 | "reference": "1057e87364c0b38b50f5695fc9df9dd189036bec" 667 | }, 668 | "dist": { 669 | "type": "zip", 670 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/1057e87364c0b38b50f5695fc9df9dd189036bec", 671 | "reference": "1057e87364c0b38b50f5695fc9df9dd189036bec", 672 | "shasum": "" 673 | }, 674 | "require": { 675 | "php": ">=5.3.3" 676 | }, 677 | "type": "library", 678 | "extra": { 679 | "branch-alias": { 680 | "dev-master": "2.5-dev" 681 | } 682 | }, 683 | "autoload": { 684 | "psr-0": { 685 | "Symfony\\Component\\Yaml\\": "" 686 | } 687 | }, 688 | "notification-url": "https://packagist.org/downloads/", 689 | "license": [ 690 | "MIT" 691 | ], 692 | "authors": [ 693 | { 694 | "name": "Fabien Potencier", 695 | "email": "fabien@symfony.com", 696 | "homepage": "http://fabien.potencier.org", 697 | "role": "Lead Developer" 698 | }, 699 | { 700 | "name": "Symfony Community", 701 | "homepage": "http://symfony.com/contributors" 702 | } 703 | ], 704 | "description": "Symfony Yaml Component", 705 | "homepage": "http://symfony.com", 706 | "time": "2014-07-08 12:21:33" 707 | } 708 | ], 709 | "aliases": [ 710 | 711 | ], 712 | "minimum-stability": "stable", 713 | "stability-flags": { 714 | "phpunit/phpunit": 0 715 | }, 716 | "platform": { 717 | "php": ">=5.3.2" 718 | }, 719 | "platform-dev": [ 720 | 721 | ] 722 | } 723 | -------------------------------------------------------------------------------- /doc/config/README: -------------------------------------------------------------------------------- 1 | Documentation 2 | ============= 3 | 4 | The documentation of this project follows the semantics and paradigms of PHPDocumentor[1]. 5 | Since the API documentation changes constantly and it is easy to build a rendered documentation 6 | is not part of the repository. 7 | 8 | How to use the configuration file to generate the documentation 9 | --------------------------------------------------------------- 10 | 11 | 1. copy the phpdoc.example.ini to a new file (e.g. phpdoc.ini) 12 | 2. replace the marked entries with your crendentials 13 | 3. save your configuration file 14 | 4. use: 15 | $>phpdoc -c doc/config/phpdoc.ini 16 | to render the documentation when your working directory is the project root directory. 17 | 18 | NOTE: 19 | Please do not commit your copy of the configuration file or the generated documenation. 20 | 21 | 22 | Links 23 | ----- 24 | 25 | [1] http://phpdoc.org 26 | -------------------------------------------------------------------------------- /doc/config/phpdoc.example.ini: -------------------------------------------------------------------------------- 1 | ;; phpDocumentor parse configuration file 2 | ;; 3 | ;; This file is designed to cut down on repetitive typing on the command-line or web interface 4 | ;; You can copy this file to create a number of configuration files that can be used with the 5 | ;; command-line switch -c, as in phpdoc -c default.ini or phpdoc -c myini.ini. The web 6 | ;; interface will automatically generate a list of .ini files that can be used. 7 | ;; 8 | ;; default.ini is used to generate the online manual at http://www.phpdoc.org/docs 9 | ;; 10 | ;; ALL .ini files must be in the user subdirectory of phpDocumentor with an extension of .ini 11 | ;; 12 | ;; Copyright 2002, Greg Beaver 13 | ;; 14 | ;; WARNING: do not change the name of any command-line parameters, phpDocumentor will ignore them 15 | 16 | [Parse Data] 17 | ;; title of all the documentation 18 | ;; legal values: any string 19 | title = {HEADLINE_OF_THE_RENDERED_DOCU} 20 | 21 | ;; parse files that start with a . like .bash_profile 22 | ;; legal values: true, false 23 | hidden = false 24 | 25 | ;; show elements marked @access private in documentation by setting this to on 26 | ;; legal values: on, off 27 | parseprivate = off 28 | 29 | ;; parse with javadoc-like description (first sentence is always the short description) 30 | ;; legal values: on, off 31 | javadocdesc = off 32 | 33 | ;; add any custom @tags separated by commas here 34 | ;; legal values: any legal tagname separated by commas. 35 | ;customtags = mytag1,mytag2 36 | 37 | ;; This is only used by the XML:DocBook/peardoc2 converter 38 | defaultcategoryname = {YOUR_DEFAULT_CATEGORYNAME} 39 | 40 | ;; what is the main package? 41 | ;; legal values: alphanumeric string plus - and _ 42 | defaultpackagename = testing 43 | 44 | ;; output any parsing information? set to on for cron jobs 45 | ;; legal values: on 46 | ;quiet = on 47 | 48 | ;; parse a PEAR-style repository. Do not turn this on if your project does 49 | ;; not have a parent directory named "pear" 50 | ;; legal values: on/off 51 | ;pear = on 52 | 53 | ;; where should the documentation be written? 54 | ;; legal values: a legal path 55 | ;target = /home/cellog/output 56 | target = {FULLPATH/TO/WHERE/THE/DOCU/SHALL/BE/RENDERED/TO} 57 | 58 | ;; Which files should be parsed out as special documentation files, such as README, 59 | ;; INSTALL and CHANGELOG? This overrides the default files found in 60 | ;; phpDocumentor.ini (this file is not a user .ini file, but the global file) 61 | readmeinstallchangelog = README, INSTALL, FAQ, LICENSE 62 | 63 | ;; limit output to the specified packages, even if others are parsed 64 | ;; legal values: package names separated by commas 65 | ;packageoutput = package1,package2 66 | 67 | ;; comma-separated list of files to parse 68 | ;; legal values: paths separated by commas 69 | ;filename = /path/to/file1,/path/to/file2,fileincurrentdirectory 70 | 71 | ;; comma-separated list of directories to parse 72 | ;; legal values: directory paths separated by commas 73 | ;directory = /path1,/path2,.,..,subdirectory 74 | ;directory = /home/jeichorn/cvs/pear 75 | ;directory = /home/cellog/workspace/phpdoc 76 | directory = {FULLPATH/TO/YOUR/SOURCE/CODE} 77 | 78 | ;; template base directory (the equivalent directory of /phpDocumentor) 79 | ;templatebase = /path/to/my/templates 80 | 81 | ;; directory to find any example files in through @example and {@example} tags 82 | ;examplesdir = /path/to/my/templates 83 | 84 | ;; comma-separated list of files, directories or wildcards ? and * (any wildcard) to ignore 85 | ;; legal values: any wildcard strings separated by commas 86 | ;; remember, this pathing is RELATIVE to the top-most directory in your "directory" value 87 | ;ignore = path/to/ignore*,*list.php,myfile.php,subdirectory/ 88 | ;ignore = pear-*,templates/,Documentation/,test*.php,Lexer.inc 89 | 90 | ;; comma-separated list of Converters to use in outputformat:Convertername:templatedirectory format 91 | ;; legal values: HTML:frames:default,HTML:frames:l0l33t,HTML:frames:phpdoc.de,HTML:frames:phphtmllib, 92 | ;; HTML:frames:earthli, 93 | ;; HTML:frames:DOM/default,HTML:frames:DOM/l0l33t,HTML:frames:DOM/phpdoc.de, 94 | ;; HTML:frames:DOM/phphtmllib,HTML:frames:DOM/earthli 95 | ;; HTML:Smarty:default,HTML:Smarty:PHP,HTML:Smarty:HandS 96 | ;; PDF:default:default,CHM:default:default,XML:DocBook/peardoc2:default 97 | output=HTML:Smarty:PHP 98 | 99 | ;; turn this option on if you want highlighted source code for every file 100 | ;; legal values: on/off 101 | sourcecode = off 102 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | Tests 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | src/lapistano/ProxyObject 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/lapistano/ProxyObject/Generator.php: -------------------------------------------------------------------------------- 1 | . 10 | * All rights reserved. 11 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License Version 2.0, January 2004 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | * 24 | * @copyright 2010-2011 Bastian Feder 25 | * @author Bastian Feder 26 | * @author Thomas Weinert 27 | * @license http://www.apache.org/licenses/LICENSE-2.0 28 | * @link https://github.com/lapistano/proxy-object 29 | * @package Unittests 30 | * @subpackage ProxyObject 31 | */ 32 | 33 | namespace lapistano\ProxyObject; 34 | 35 | /** 36 | * Path to this module. 37 | * @var string 38 | */ 39 | $modulePath = __DIR__; 40 | 41 | if (version_compare(\PHPUnit_Runner_Version::id(), '3.5', '<')) { 42 | include_once('PHPUnit/Util/Class.php'); 43 | include_once('PHPUnit/Util/Filter.php'); 44 | include_once('PHPUnit/Framework/Exception.php'); 45 | } 46 | 47 | /** 48 | * Class used by the ProxyObject to generate the actual proxy pbject. 49 | * 50 | * @copyright 2010-2011 Bastian Feder 51 | * @author Bastian Feder 52 | * @author Thomas Weinert 53 | * @license http://www.apache.org/licenses/LICENSE-2.0 54 | * @link https://github.com/lapistano/proxy-object 55 | * @package Unittests 56 | * @subpackage ProxyObject 57 | * 58 | */ 59 | class Generator 60 | { 61 | /** 62 | * Contain elements worth caching. 63 | * @var array 64 | */ 65 | protected static $cache = array(); 66 | 67 | /** 68 | * List of methods not to be reflected. 69 | * @var array 70 | */ 71 | protected static $blacklistedMethodNames = array( 72 | '__clone' => true, 73 | '__destruct' => true, 74 | 'abstract' => true, 75 | 'and' => true, 76 | 'array' => true, 77 | 'as' => true, 78 | 'break' => true, 79 | 'case' => true, 80 | 'catch' => true, 81 | 'class' => true, 82 | 'clone' => true, 83 | 'const' => true, 84 | 'continue' => true, 85 | 'declare' => true, 86 | 'default' => true, 87 | 'do' => true, 88 | 'else' => true, 89 | 'elseif' => true, 90 | 'enddeclare' => true, 91 | 'endfor' => true, 92 | 'endforeach' => true, 93 | 'endif' => true, 94 | 'endswitch' => true, 95 | 'endwhile' => true, 96 | 'extends' => true, 97 | 'final' => true, 98 | 'for' => true, 99 | 'foreach' => true, 100 | 'function' => true, 101 | 'global' => true, 102 | 'goto' => true, 103 | 'if' => true, 104 | 'implements' => true, 105 | 'interface' => true, 106 | 'instanceof' => true, 107 | 'namespace' => true, 108 | 'new' => true, 109 | 'or' => true, 110 | 'private' => true, 111 | 'protected' => true, 112 | 'public' => true, 113 | 'static' => true, 114 | 'switch' => true, 115 | 'throw' => true, 116 | 'try' => true, 117 | 'use' => true, 118 | 'var' => true, 119 | 'while' => true, 120 | 'xor' => true 121 | ); 122 | 123 | /** 124 | * Switch to decide if dedicated properties are to be exposed or not. 125 | * @var boolean 126 | */ 127 | protected static $exposeProperties = false; 128 | 129 | /** 130 | * Switch to decide if dedicated methods are to be exposed or not. 131 | * @var boolean 132 | */ 133 | protected static $exposeMethods = false; 134 | 135 | /** 136 | * Gets the data to be used for the actual reflection. 137 | * 138 | * If the class has already been reflected in the same configuration, 139 | * it will be fetched from the local cache. 140 | * 141 | * @static 142 | * 143 | * @param string $originalClassName Name of the class to be reflected. 144 | * @param array|null $methods (Optional) List of methods to be exposed. 145 | * @param array|null $properties (Optional) List of properties to be exposed. 146 | * @param string $proxyClassName (Optional) Name to be used for the reflected class. 147 | * @param bool $callAutoload (Optional) Switch to run the autoloader. 148 | * 149 | * @return array The data to be used for the actual reflection. 150 | */ 151 | public static function generate($originalClassName, array $methods = null, array $properties = null, 152 | $proxyClassName = '', $callAutoload = false) 153 | { 154 | 155 | self::$exposeMethods = false; 156 | self::$exposeProperties = false; 157 | 158 | if ($proxyClassName == '') { 159 | $key = md5( 160 | $originalClassName . serialize($methods) . serialize($properties) 161 | ); 162 | 163 | if (isset(self::$cache[$key])) { 164 | return self::$cache[$key]; 165 | } 166 | } 167 | 168 | if (!empty($methods)) { 169 | self::$exposeMethods = true; 170 | } 171 | 172 | if (!empty($properties)) { 173 | self::$exposeProperties = true; 174 | } 175 | 176 | $proxy = self::generateProxy( 177 | $originalClassName, $methods, $properties, $proxyClassName, $callAutoload 178 | ); 179 | 180 | if (isset($key)) { 181 | self::$cache[$key] = $proxy; 182 | } 183 | 184 | return $proxy; 185 | } 186 | 187 | /** 188 | * Gets the arguments the proxied method expectes. 189 | * 190 | * @param \ReflectionMethod $method 191 | * 192 | * @return array List of parameters to be passed to the proxied method. 193 | */ 194 | public static function getMethodCallParameters($method) 195 | { 196 | $parameters = array(); 197 | foreach ($method->getParameters() as $i => $parameter) { 198 | $parameters[] = '$' . $parameter->getName(); 199 | } 200 | 201 | return join(', ', $parameters); 202 | } 203 | 204 | /** 205 | * Generates the data to be used for the actual reflection. 206 | * 207 | * @static 208 | * 209 | * @param string $originalClassName Name of the class to be reflected. 210 | * @param array|null $methods (Optional) List of methods to be exposed. 211 | * @param array|null $properties (Optional) List of properties to be exposed. 212 | * @param string $proxyClassName (Optional) Name to be used for the reflected class. 213 | * @param bool $callAutoload (Optional) Switch to run the autoloader. 214 | * 215 | * @return array The data to be used for the actual reflection. 216 | * @throws \lapistano\ProxyObject\GeneratorException 217 | */ 218 | protected static function generateProxy($originalClassName, array $methods = null, array $properties = null, 219 | $proxyClassName = '', $callAutoload = false) 220 | { 221 | $templateDir = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR; 222 | $classTemplate = self::createTemplateObject( 223 | $templateDir . 'proxied_class.tpl' 224 | ); 225 | 226 | $proxyClassName = self::generateProxyClassName( 227 | $originalClassName, $proxyClassName 228 | ); 229 | 230 | if (interface_exists($proxyClassName['fullClassName'], $callAutoload)) { 231 | throw new GeneratorException(sprintf( 232 | '"%s" is an interface.', $proxyClassName['fullClassName'] 233 | ), GeneratorException::IS_INTERFACE); 234 | } 235 | 236 | if (!class_exists($proxyClassName['fullClassName'], $callAutoload)) { 237 | throw new GeneratorException(sprintf( 238 | 'Class "%s" does not exists.', $proxyClassName['fullClassName'] 239 | ), GeneratorException::CLASS_NOT_FOUND); 240 | } 241 | 242 | $class = new \ReflectionClass($proxyClassName['fullClassName']); 243 | 244 | if ($class->isFinal()) { 245 | throw new GeneratorException(sprintf( 246 | 'Class "%s" is declared "final". Cannot create proxy.', $proxyClassName['fullClassName'] 247 | ), GeneratorException::CLASS_IS_FINAL); 248 | } 249 | 250 | if (!empty($proxyClassName['namespaceName'])) { 251 | $prologue = 'namespace ' . $proxyClassName['namespaceName'] . ";\n\n"; 252 | } 253 | 254 | $classTemplate->setVar( 255 | array( 256 | 'prologue' => isset($prologue) ? $prologue : '', 257 | 'class_declaration' => $proxyClassName['proxyClassName'] . ' extends ' . $originalClassName, 258 | 'methods' => self::getProxiedMethods($proxyClassName['fullClassName'], $class, $methods), 259 | 'properties' => self::getProxiedProperties($proxyClassName['fullClassName'], $class, $properties), 260 | ) 261 | ); 262 | 263 | return array( 264 | 'code' => $classTemplate->render(), 265 | 'proxyClassName' => $proxyClassName['proxyClassName'], 266 | 'namespaceName' => $proxyClassName['namespaceName'] 267 | ); 268 | } 269 | 270 | /** 271 | * Generate string representing the set of properties to be reflected. 272 | * 273 | * @param string $fullClassName 274 | * @param \ReflectionClass $class 275 | * @param array $properties 276 | * 277 | * @return string 278 | * @throws \lapistano\ProxyObject\GeneratorException 279 | */ 280 | protected static function getProxiedProperties($fullClassName, \ReflectionClass $class, array $properties = null) 281 | { 282 | $proxiedProperties = ''; 283 | $templateDir = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR; 284 | $proxyProperties = $class->getProperties(\ReflectionMethod::IS_PROTECTED | \ReflectionMethod::IS_PRIVATE); 285 | 286 | if (empty($properties)) { 287 | foreach ($proxyProperties as $property) { 288 | $proxiedProperties .= self::generateProxiedPropertyDefinition($templateDir, $property, $class); 289 | } 290 | } else { 291 | 292 | foreach ($properties as $propertyName) { 293 | 294 | if ($class->hasProperty($propertyName)) { 295 | $property = $class->getProperty($propertyName); 296 | $proxiedProperties .= self::generateProxiedPropertyDefinition($templateDir, $property, $class); 297 | continue; 298 | } 299 | 300 | throw new GeneratorException( 301 | sprintf( 302 | 'Class "%s" has no protected or private property "%s".', $fullClassName, $propertyName 303 | ), 304 | GeneratorException::NO_PROTECTED_OR_PRIVATE_PROPERTY_DEFINED 305 | ); 306 | } 307 | } 308 | 309 | return $proxiedProperties; 310 | } 311 | 312 | /** 313 | * Generates the definition of a method to be proxied. 314 | * 315 | * @param string $templateDir Location of the templates to be used to create the proxy. 316 | * @param \ReflectionProperty $property Name of the property to be reflected. 317 | * @param \ReflectionClass $class Name of the class to be reflected. 318 | * 319 | * @return array Information about the method to be proxied. 320 | */ 321 | protected static function generateProxiedPropertyDefinition($templateDir, \ReflectionProperty $property, 322 | \ReflectionClass $class) 323 | { 324 | $template = self::createTemplateObject( 325 | $templateDir . 'proxied_property.tpl' 326 | ); 327 | 328 | $property->setAccessible(true); 329 | $properties = $class->getDefaultProperties(); 330 | $value = $properties[$property->getName()]; 331 | 332 | if ($value === (array)$value) { 333 | $value = self::arrayToString($value); 334 | $value = sprintf(' = array(%s)', $value); 335 | } else { 336 | if ($value === (string)$value) { 337 | $value = sprintf(' = \'%s\'', $value); 338 | } else { 339 | if (is_object($value)) { 340 | $value = ''; 341 | } else { 342 | if ($value === (float)$value) { 343 | $value = sprintf(' = %f', $value); 344 | } 345 | } 346 | } 347 | } 348 | 349 | $template->setVar( 350 | array( 351 | 'keyword' => $property->isStatic() ? 'static ' : '', 352 | 'property_name' => $property->getName(), 353 | 'property_value' => $value 354 | ) 355 | ); 356 | 357 | return $template->render(); 358 | } 359 | 360 | /** 361 | * Generate string representing the set of methods to be reflected. 362 | * 363 | * @param array $fullClassName 364 | * @param \ReflectionClass $class 365 | * @param array $methods 366 | * 367 | * @return array Information about the method to be proxied. 368 | * 369 | * @throws GeneratorException 370 | */ 371 | protected static function getProxiedMethods($fullClassName, \ReflectionClass $class, array $methods = null) 372 | { 373 | $proxyMethods = array(); 374 | $proxiedMethods = ''; 375 | $templateDir = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR; 376 | 377 | if (is_array($methods) && count($methods) > 0) { 378 | 379 | foreach ($methods as $methodName) { 380 | 381 | if ($class->hasMethod($methodName)) { 382 | 383 | $method = $class->getMethod($methodName); 384 | 385 | if (self::canProxyMethod($method)) { 386 | $proxyMethods[] = $method; 387 | continue; 388 | } 389 | 390 | throw new GeneratorException( 391 | sprintf('Can not proxy method "%s" of class "%s".', $methodName, $fullClassName), 392 | GeneratorException::CANNOT_PROXY_METHOD 393 | ); 394 | } 395 | 396 | throw new GeneratorException( 397 | sprintf('Class "%s" has no protected method "%s".', $fullClassName, $methodName), 398 | GeneratorException::NO_PROTECTED_METHOD_DEFINED 399 | ); 400 | } 401 | } else { 402 | 403 | $proxyMethods = self::canProxyMethods($class->getMethods(\ReflectionMethod::IS_PROTECTED)); 404 | 405 | if (empty($proxyMethods ) && self::$exposeProperties === false) { 406 | throw new GeneratorException( 407 | sprintf('Class "%s" has no protected methods.', $fullClassName), 408 | GeneratorException::NO_PROTECTED_METHOD_DEFINED 409 | ); 410 | } 411 | } 412 | 413 | foreach ($proxyMethods as $method) { 414 | $proxiedMethods .= self::generateProxiedMethodDefinition($templateDir, $method); 415 | } 416 | 417 | return $proxiedMethods; 418 | } 419 | 420 | /** 421 | * Generates a unique name to be used to identify the created proxyclass. 422 | * 423 | * @param string $originalClassName Name of the class to be reflected. 424 | * @param string $proxyClassName Name to be used for the reflected class. 425 | * 426 | * @return array Information of the class to be reflected. 427 | */ 428 | protected static function generateProxyClassName($originalClassName, $proxyClassName) 429 | { 430 | $classNameParts = explode('\\', $originalClassName); 431 | $namespaceName = ''; 432 | $fullClassName = $originalClassName; 433 | 434 | if (count($classNameParts) > 1) { 435 | $originalClassName = array_pop($classNameParts); 436 | $namespaceName = implode('\\', $classNameParts); 437 | $fullClassName = $namespaceName . '\\' . $originalClassName; 438 | 439 | // eval does identifies namespaces with leading backslash as constant. 440 | $namespaceName = (0 === stripos($namespaceName, '\\') ? substr($namespaceName, 1) : $namespaceName); 441 | } 442 | 443 | if ($proxyClassName == '') { 444 | do { 445 | $proxyClassName = 'Proxy_' . $originalClassName . '_' . substr(md5(microtime()), 0, 8); 446 | } while (class_exists($proxyClassName, false)); 447 | } 448 | 449 | return array( 450 | 'proxyClassName' => $proxyClassName, 451 | 'className' => $originalClassName, 452 | 'fullClassName' => $fullClassName, 453 | 'namespaceName' => $namespaceName 454 | ); 455 | } 456 | 457 | /** 458 | * Generates the definition of a method to be proxied. 459 | * 460 | * @param string $templateDir Location of the templates to be used to create the proxy. 461 | * @param \ReflectionMethod $method Name of the method to be reflected. 462 | * 463 | * @return array Information about the method to be proxied. 464 | */ 465 | protected static function generateProxiedMethodDefinition($templateDir, \ReflectionMethod $method) 466 | { 467 | $reference = ''; 468 | 469 | if ($method->returnsReference()) { 470 | $reference = '&'; 471 | } 472 | 473 | $template = self::createTemplateObject( 474 | $templateDir . 'proxied_method.tpl' 475 | ); 476 | 477 | $template->setVar( 478 | array( 479 | 'arguments_declaration' => self::getArgumentDeclaration($method), 480 | 'arguments' => self::getMethodCallParameters($method), 481 | 'method_name' => $method->getName(), 482 | 'reference' => $reference 483 | ) 484 | ); 485 | 486 | return $template->render(); 487 | } 488 | 489 | /** 490 | * Adds prefix for global namespace to the type hint of each parameter of the method. 491 | * 492 | * This method is only necessary 'cause PHPUnit is currently not able to detect the usage of 493 | * namespaces. Thus it does not add the '\' to a type hint. 494 | * 495 | * @param \ReflectionMethod $method 496 | * 497 | * @return string 498 | */ 499 | protected static function getArgumentDeclaration(\ReflectionMethod $method) 500 | { 501 | $declarations = array(); 502 | $parameters = explode(', ', self::getMethodParameters($method)); 503 | 504 | foreach ($parameters as $parameter) { 505 | 506 | if (0 < strpos(trim($parameter), ' $') && false === strpos($parameter, 'array')) { 507 | $declarations[] = '\\' . $parameter; 508 | continue; 509 | } 510 | 511 | $declarations[] = $parameter; 512 | } 513 | 514 | return implode(', ', $declarations); 515 | } 516 | 517 | /** 518 | * Determine if the given method may be proxied. 519 | * 520 | * Since it is not possible to reflect a 521 | * - constructor 522 | * - final method 523 | * - static method 524 | * those will cause this method to return false. 525 | * Also methods registered in the blacklist will cause this 526 | * method to return false. 527 | * 528 | * @param \ReflectionMethod $method Name of the method to be reflected. 529 | * 530 | * @return boolean True, if the given method may be reflected, else false. 531 | */ 532 | protected static function canProxyMethod(\ReflectionMethod $method) 533 | { 534 | if ($method->isConstructor() || $method->isFinal() || $method->isStatic() || isset(self::$blacklistedMethodNames[$method->getName()]) 535 | ) { 536 | return false; 537 | } elseif ($method->isProtected()) { 538 | return true; 539 | } 540 | 541 | return false; 542 | } 543 | 544 | /** 545 | * Filters the list of ReflectionMethods down to those actually proxyable. 546 | * 547 | * Proxyable methods are those which are NOT: 548 | * - constructor 549 | * - final method 550 | * - static method 551 | * - members of the self::blacklist 552 | * 553 | * @param array $methods 554 | * 555 | * @return \ReflectionMethod[] 556 | */ 557 | protected static function canProxyMethods(array $methods) 558 | { 559 | $proxyMethods = array(); 560 | 561 | foreach ($methods as $method) { 562 | if (self::canProxyMethod($method)) { 563 | $proxyMethods[] = $method; 564 | } 565 | } 566 | 567 | return $proxyMethods; 568 | } 569 | 570 | /** 571 | * Generates the template to be used to create a proxy object. 572 | * 573 | * The return value depends on the used version of PHPUnit. 574 | * If a version is 3.5 of higher Text_Template, else PHPUnit_Util_Template 575 | * is used. 576 | * 577 | * @param string $file The location of the template file to be used. 578 | * 579 | * @return \Text_Template The template object to create the proxy class. 580 | */ 581 | protected static function createTemplateObject($file) 582 | { 583 | @include_once('Text/Template.php'); 584 | 585 | return new \Text_Template($file); 586 | } 587 | 588 | /** 589 | * Converts the given array to its string represenation recursivly. 590 | * 591 | * @param array $value 592 | * 593 | * @return string 594 | */ 595 | protected static function arrayToString(array $value) 596 | { 597 | if (empty($value)) { 598 | return ''; 599 | } 600 | 601 | return self::traverseStructure(new \RecursiveArrayIterator($value)); 602 | } 603 | 604 | /** 605 | * Walks recursivly throught an array and concatinates the found info to a string. 606 | * 607 | * @param \RecursiveIterator $iterator 608 | * 609 | * @return string 610 | */ 611 | protected static function traverseStructure($iterator) 612 | { 613 | $children = ''; 614 | while ($iterator->valid()) { 615 | 616 | if ($iterator->hasChildren()) { 617 | $current = 'array (' . self::traverseStructure($iterator->getChildren()) . '), '; 618 | } else { 619 | $current = "'" . $iterator->current() . "', "; 620 | } 621 | 622 | $key = $iterator->key(); 623 | 624 | if (is_numeric($key)) { 625 | $children .= $key . " => " . $current; 626 | } else { 627 | $children .= "'" . $key . "' => " . $current; 628 | } 629 | 630 | $iterator->next(); 631 | } 632 | 633 | return $children; 634 | } 635 | 636 | 637 | /** 638 | * Returns the parameters of a function or method. 639 | * 640 | * @param \ReflectionFunction|\ReflectionMethod $method 641 | * @param boolean $forCall 642 | * @return string 643 | * @since Method available since Release 3.2.0 644 | */ 645 | public static function getMethodParameters($method, $forCall = FALSE) 646 | { 647 | $parameters = array(); 648 | 649 | foreach ($method->getParameters() as $i => $parameter) { 650 | $name = '$' . $parameter->getName(); 651 | 652 | /* Note: PHP extensions may use empty names for reference arguments 653 | * or "..." for methods taking a variable number of arguments. 654 | */ 655 | if ($name === '$' || $name === '$...') { 656 | $name = '$arg' . $i; 657 | } 658 | 659 | $default = ''; 660 | $reference = ''; 661 | $typeHint = ''; 662 | 663 | if (!$forCall) { 664 | if ($parameter->isArray()) { 665 | $typeHint = 'array '; 666 | } 667 | 668 | else if (version_compare(PHP_VERSION, '5.4.0', '>=') && $parameter->isCallable()) { 669 | $typeHint = 'callable '; 670 | } 671 | 672 | else { 673 | try { 674 | $class = $parameter->getClass(); 675 | } 676 | 677 | catch (\ReflectionException $e) { 678 | $class = FALSE; 679 | } 680 | 681 | if ($class) { 682 | $typeHint = $class->getName() . ' '; 683 | } 684 | } 685 | 686 | if ($parameter->isDefaultValueAvailable()) { 687 | $value = $parameter->getDefaultValue(); 688 | $default = ' = ' . var_export($value, TRUE); 689 | } 690 | 691 | else if ($parameter->isOptional()) { 692 | $default = ' = null'; 693 | } 694 | 695 | if ($parameter->isPassedByReference()) { 696 | $reference = '&'; 697 | } 698 | } 699 | 700 | $parameters[] = $typeHint . $reference . $name . $default; 701 | } 702 | 703 | return join(', ', $parameters); 704 | } 705 | } 706 | -------------------------------------------------------------------------------- /src/lapistano/ProxyObject/Generator/proxied_class.tpl: -------------------------------------------------------------------------------- 1 | {prologue}class {class_declaration} 2 | { 3 | 4 | {properties} 5 | 6 | {methods} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/lapistano/ProxyObject/Generator/proxied_method.tpl: -------------------------------------------------------------------------------- 1 | public function {reference}{method_name}({arguments_declaration}) 2 | { 3 | return parent::{method_name}({arguments}); 4 | } 5 | -------------------------------------------------------------------------------- /src/lapistano/ProxyObject/Generator/proxied_property.tpl: -------------------------------------------------------------------------------- 1 | public {keyword}${property_name}{property_value}; 2 | -------------------------------------------------------------------------------- /src/lapistano/ProxyObject/GeneratorException.php: -------------------------------------------------------------------------------- 1 | . 6 | * All rights reserved. 7 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License Version 2.0, January 2004 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * @copyright 2010-2011 Bastian Feder 21 | * @author Bastian Feder 22 | * @license http://www.apache.org/licenses/LICENSE-2.0 23 | * @link https://github.com/lapistano/proxy-object 24 | * @package Unittests 25 | * @subpackage ProxyObject 26 | */ 27 | 28 | namespace lapistano\ProxyObject; 29 | 30 | class GeneratorException extends \PHPUnit_Framework_Exception 31 | { 32 | const NO_PROTECTED_METHOD_DEFINED = 100; 33 | const CANNOT_PROXY_METHOD = 101; 34 | const CLASS_NOT_FOUND = 102; 35 | const CLASS_IS_FINAL = 103; 36 | const IS_INTERFACE = 104; 37 | const NO_PROTECTED_OR_PRIVATE_PROPERTY_DEFINED = 105; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/lapistano/ProxyObject/ProxyBuilder.php: -------------------------------------------------------------------------------- 1 | . 8 | * All rights reserved. 9 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License Version 2.0, January 2004 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | * 22 | * @copyright 2010-2011 Bastian Feder 23 | * @author Bastian Feder 24 | * @license http://www.apache.org/licenses/LICENSE-2.0 25 | * @link https://github.com/lapistano/proxy-object 26 | * @package Unittests 27 | * @subpackage ProxyObject 28 | */ 29 | namespace lapistano\ProxyObject; 30 | 31 | /** 32 | * Implementation of the Builder pattern for Proxy objects. 33 | * 34 | * @copyright 2010-2011 Bastian Feder 35 | * @author Bastian Feder 36 | * @license http://www.apache.org/licenses/LICENSE-2.0 37 | * @link https://github.com/lapistano/proxy-object 38 | * @package Unittests 39 | * @subpackage ProxyObject 40 | */ 41 | class ProxyBuilder 42 | { 43 | /** 44 | * @var string 45 | */ 46 | protected $className; 47 | 48 | /** 49 | * @var array 50 | */ 51 | protected $methods = array(); 52 | 53 | /** 54 | * @var array 55 | */ 56 | protected $properties = array(); 57 | 58 | /** 59 | * @var string 60 | */ 61 | protected $proxyClassName = ''; 62 | 63 | /** 64 | * @var array 65 | */ 66 | protected $constructorArgs = array(); 67 | 68 | /** 69 | * @var boolean 70 | */ 71 | protected $autoload = true; 72 | 73 | /** 74 | * @var boolean 75 | */ 76 | protected $invokeOriginalConstructor = true; 77 | 78 | /** 79 | * @var string 80 | */ 81 | protected $mockClassName; 82 | 83 | /** 84 | * @param string $className 85 | */ 86 | public function __construct($className) 87 | { 88 | $this->className = $className; 89 | } 90 | 91 | /** 92 | * Creates a proxy object using a fluent interface. 93 | * 94 | * @return object Instance of the proxied class exposing the configured attributes and methods. 95 | */ 96 | public function getProxy() 97 | { 98 | include_once(__DIR__ . '/Generator.php'); 99 | 100 | $proxyClass = Generator::generate( 101 | $this->className, $this->methods, $this->properties, $this->proxyClassName, $this->autoload 102 | ); 103 | 104 | $classname = $proxyClass['proxyClassName']; 105 | 106 | if (!empty($proxyClass['namespaceName'])) { 107 | $classname = $proxyClass['namespaceName'] . '\\' . $proxyClass['proxyClassName']; 108 | } 109 | 110 | if (!class_exists($classname, false)) { 111 | eval($proxyClass['code']); 112 | } 113 | 114 | if ($this->invokeOriginalConstructor && !interface_exists($this->className, $this->autoload)) { 115 | 116 | if (empty($this->constructorArgs)) { 117 | return new $classname(); 118 | } 119 | 120 | $proxy = new \ReflectionClass($classname); 121 | 122 | return $proxy->newInstanceArgs($this->constructorArgs); 123 | } 124 | 125 | return $this->getInstanceOf($classname); 126 | } 127 | 128 | /** 129 | * Specifies the subset of methods to proxy. Default is to proxy all of them. 130 | * 131 | * @param array $methods 132 | * 133 | * @return ProxyBuilder 134 | */ 135 | public function setMethods(array $methods) 136 | { 137 | $this->methods = $methods; 138 | 139 | return $this; 140 | } 141 | 142 | /** 143 | * Specifies the subset of properties to expose. Default is to proxy all of them. 144 | * 145 | * @param array $properties 146 | * 147 | * @return \lapistano\ProxyObject\ProxyBuilder 148 | */ 149 | public function setProperties(array $properties) 150 | { 151 | $this->properties = $properties; 152 | 153 | return $this; 154 | } 155 | 156 | /** 157 | * Specifies the arguments for the constructor. 158 | * 159 | * @param array $args 160 | * 161 | * @return \lapistano\ProxyObject\ProxyBuilder 162 | */ 163 | public function setConstructorArgs(array $args) 164 | { 165 | $this->constructorArgs = $args; 166 | 167 | return $this; 168 | } 169 | 170 | /** 171 | * Specifies the name for the proxy class. 172 | * 173 | * @param string $name 174 | * 175 | * @return \lapistano\ProxyObject\ProxyBuilder 176 | */ 177 | public function setProxyClassName($name) 178 | { 179 | $this->mockClassName = $name; 180 | 181 | return $this; 182 | } 183 | 184 | /** 185 | * Suppresses the invocation of the original constructor. 186 | * 187 | * @return \lapistano\ProxyObject\ProxyBuilder 188 | */ 189 | public function disableOriginalConstructor() 190 | { 191 | $this->invokeOriginalConstructor = false; 192 | 193 | return $this; 194 | } 195 | 196 | /** 197 | * Suppresses the use of class autoloading while creating the proxy object. 198 | * 199 | * @return \lapistano\ProxyObject\ProxyBuilder 200 | */ 201 | public function disableAutoload() 202 | { 203 | $this->autoload = false; 204 | 205 | return $this; 206 | } 207 | 208 | /** 209 | * Provides an instance of the given class without invoking it's constructor 210 | * 211 | * @param string $classname 212 | * 213 | * @return object 214 | */ 215 | protected function getInstanceOf($classname) 216 | { 217 | // As of PHP5.4 the reflection api provides a way to get an instance 218 | // of a class without invoking the constructor. 219 | if (method_exists('ReflectionClass', 'newInstanceWithoutConstructor')) { 220 | $reflectedClass = new \ReflectionClass($classname); 221 | 222 | return $reflectedClass->newInstanceWithoutConstructor(); 223 | } 224 | 225 | // Use a trick to create a new object of a class 226 | // without invoking its constructor. 227 | return unserialize( 228 | sprintf( 229 | 'O:%d:"%s":0:{}', strlen($classname), $classname 230 | ) 231 | ); 232 | } 233 | } 234 | --------------------------------------------------------------------------------