|
2 |
+<?php |
|
3 |
+/** |
|
4 |
+ * This file is part of PHP_Depend. |
|
5 |
+ * |
|
6 |
+ * PHP Version 5 |
|
7 |
+ * |
|
8 |
+ * Copyright (c) 2008-2009, Manuel Pichler <mapi@pdepend.org>. |
|
9 |
+ * All rights reserved. |
|
10 |
+ * |
|
11 |
+ * Redistribution and use in source and binary forms, with or without |
|
12 |
+ * modification, are permitted provided that the following conditions |
|
13 |
+ * are met: |
|
14 |
+ * |
|
15 |
+ * * Redistributions of source code must retain the above copyright |
|
16 |
+ * notice, this list of conditions and the following disclaimer. |
|
17 |
+ * |
|
18 |
+ * * Redistributions in binary form must reproduce the above copyright |
|
19 |
+ * notice, this list of conditions and the following disclaimer in |
|
20 |
+ * the documentation and/or other materials provided with the |
|
21 |
+ * distribution. |
|
22 |
+ * |
|
23 |
+ * * Neither the name of Manuel Pichler nor the names of his |
|
24 |
+ * contributors may be used to endorse or promote products derived |
|
25 |
+ * from this software without specific prior written permission. |
|
26 |
+ * |
|
27 |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
28 |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
29 |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
30 |
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|
31 |
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
32 |
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|
33 |
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
34 |
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
35 |
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
36 |
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
37 |
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
38 |
+ * POSSIBILITY OF SUCH DAMAGE. |
|
39 |
+ * |
|
40 |
+ * @category PHP |
|
41 |
+ * @package PHP_Depend |
|
42 |
+ * @subpackage Bugs |
|
43 |
+ * @author Manuel Pichler <mapi@pdepend.org> |
|
44 |
+ * @copyright 2008-2009 Manuel Pichler. All rights reserved. |
|
45 |
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License |
|
46 |
+ * @version SVN: $Id$ |
|
47 |
+ * @link http://www.pdepend.org/ |
|
48 |
+ */ |
|
49 |
+ |
|
50 |
+require_once dirname(__FILE__) . '/AbstractTest.php'; |
|
51 |
+ |
|
52 |
+/** |
|
53 |
+ * Tests that the parser handles a closure that returns a reference correct. |
|
54 |
+ * This test is related to bug #94. |
|
55 |
+ * |
|
56 |
+ * @category PHP |
|
57 |
+ * @package PHP_Depend |
|
58 |
+ * @subpackage Bugs |
|
59 |
+ * @author Manuel Pichler <mapi@pdepend.org> |
|
60 |
+ * @copyright 2008-2009 Manuel Pichler. All rights reserved. |
|
61 |
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License |
|
62 |
+ * @version Release: @package_version@ |
|
63 |
+ * @link http://www.pdepend.org/ |
|
64 |
+ */ |
|
65 |
+class PHP_Depend_Bugs_ClosureReturnsByReferenceBug094Test |
|
66 |
+ extends PHP_Depend_Bugs_AbstractTest |
|
67 |
+{ |
|
68 |
+ /** |
|
69 |
+ * @return void |
|
70 |
+ * @group bugs |
|
71 |
+ */ |
|
72 |
+ public function testParserHandlesClosureThatReturnsReference() |
|
73 |
+ { |
|
74 |
+ $packages = self::parseTestCaseSource(__METHOD__); |
|
75 |
+ $function = $packages->current() |
|
76 |
+ ->getFunctions() |
|
77 |
+ ->current(); |
|
78 |
+ } |
|
79 |
+} |
| 753 |
753 |
{ |
| 754 |
754 |
$this->_tokenStack->push(); |
| 755 |
755 |
|
| 756 |
|
- // Read function keyword |
| 757 |
756 |
$this->_consumeToken(self::T_FUNCTION); |
| 758 |
|
- |
| 759 |
|
- // Remove leading comments |
| 760 |
757 |
$this->_consumeComments(); |
|
758 |
+ |
|
759 |
+ $returnReference = $this->_parseOptionalReturnbyReference(); |
| 761 |
760 |
|
| 762 |
|
- // Check for closure or function |
| 763 |
|
- if ($this->_tokenizer->peek() === self::T_PARENTHESIS_OPEN) { |
|
761 |
+ if ($this->_isNextTokenFunctionOrMethodIdentifier()) { |
|
762 |
+ $callable = $this->_parseFunctionDeclaration(); |
|
763 |
+ } else { |
| 764 |
764 |
$callable = $this->_parseClosureDeclaration(); |
| 765 |
|
- } else { |
| 766 |
|
- $callable = $this->_parseFunctionDeclaration(); |
| 767 |
765 |
} |
| 768 |
766 |
|
| 769 |
767 |
$callable->setSourceFile($this->_sourceFile); |
| 771 |
769 |
$callable->setTokens($this->_tokenStack->pop()); |
| 772 |
770 |
$this->_prepareCallable($callable); |
| 773 |
771 |
|
|
772 |
+ if ($returnReference) { |
|
773 |
+ $callable->setReturnsReference(); |
|
774 |
+ } |
|
775 |
+ |
| 774 |
776 |
$this->reset(); |
| 775 |
777 |
|
| 776 |
778 |
return $callable; |
| 777 |
779 |
} |
| 778 |
780 |
|
| 779 |
781 |
/** |
|
782 |
+ * Parses an optional returns by reference token. The return value will be |
|
783 |
+ * <b>true</b> when a reference token was found, otherwise this method will |
|
784 |
+ * return <b>false</b>. |
|
785 |
+ * |
|
786 |
+ * @return boolean |
|
787 |
+ * @since 0.9.7 |
|
788 |
+ */ |
|
789 |
+ private function _parseOptionalReturnbyReference() |
|
790 |
+ { |
|
791 |
+ if ($this->_isNextTokenReturnByReference()) { |
|
792 |
+ return $this->_parseReturnByReference(); |
|
793 |
+ } |
|
794 |
+ return false; |
|
795 |
+ } |
|
796 |
+ |
|
797 |
+ /** |
|
798 |
+ * Tests that the next available token is the returns by reference token. |
|
799 |
+ * |
|
800 |
+ * @return boolean |
|
801 |
+ * @since 0.9.7 |
|
802 |
+ */ |
|
803 |
+ private function _isNextTokenReturnByReference() |
|
804 |
+ { |
|
805 |
+ return ($this->_tokenizer->peek() === self::T_BITWISE_AND); |
|
806 |
+ } |
|
807 |
+ |
|
808 |
+ |
|
809 |
+ /** |
|
810 |
+ * This method parses a returns by reference token and returns <b>true</b>. |
|
811 |
+ * |
|
812 |
+ * @return boolean |
|
813 |
+ */ |
|
814 |
+ private function _parseReturnByReference() |
|
815 |
+ { |
|
816 |
+ $this->_consumeToken(self::T_BITWISE_AND); |
|
817 |
+ $this->_consumeComments(); |
|
818 |
+ |
|
819 |
+ return true; |
|
820 |
+ } |
|
821 |
+ |
|
822 |
+ /** |
|
823 |
+ * Tests that the next available token is a function or method identifier. |
|
824 |
+ * |
|
825 |
+ * @return boolean |
|
826 |
+ * @since 0.9.7 |
|
827 |
+ */ |
|
828 |
+ private function _isNextTokenFunctionOrMethodIdentifier() |
|
829 |
+ { |
|
830 |
+ return ($this->_tokenizer->peek() === self::T_STRING); |
|
831 |
+ } |
|
832 |
+ |
|
833 |
+ /** |
| 780 |
834 |
* This method parses a function declaration. |
| 781 |
835 |
* |
| 782 |
836 |
* @return PHP_Depend_Code_Function |
| 784 |
838 |
*/ |
| 785 |
839 |
private function _parseFunctionDeclaration() |
| 786 |
840 |
{ |
| 787 |
|
- // Remove leading comments |
| 788 |
841 |
$this->_consumeComments(); |
| 789 |
842 |
|
| 790 |
|
- $returnsReference = false; |
| 791 |
|
- |
| 792 |
|
- // Check for returns reference token |
| 793 |
|
- if ($this->_tokenizer->peek() === self::T_BITWISE_AND) { |
| 794 |
|
- $this->_consumeToken(self::T_BITWISE_AND); |
| 795 |
|
- $this->_consumeComments(); |
| 796 |
|
- |
| 797 |
|
- $returnsReference = true; |
| 798 |
|
- } |
| 799 |
|
- |
| 800 |
843 |
// Next token must be the function identifier |
| 801 |
844 |
$functionName = $this->_consumeToken(self::T_STRING)->image; |
| 802 |
845 |
|
| 803 |
846 |
$function = $this->_builder->buildFunction($functionName); |
| 804 |
847 |
$this->_parseCallableDeclaration($function); |
| 805 |
848 |
|
| 806 |
|
- if ($returnsReference === true) { |
| 807 |
|
- $function->setReturnsReference(); |
| 808 |
|
- } |
| 809 |
|
- |
| 810 |
849 |
// First check for an existing namespace |
| 811 |
850 |
if ($this->_namespaceName !== null) { |
| 812 |
851 |
$packageName = $this->_namespaceName; |