1<?php
2
3/*
4 * This file is part of the Prophecy.
5 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
6 *     Marcello Duarte <marcello.duarte@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Prophecy\Doubler\Generator\Node;
13
14use Prophecy\Doubler\Generator\TypeHintReference;
15use Prophecy\Exception\InvalidArgumentException;
16
17/**
18 * Method node.
19 *
20 * @author Konstantin Kudryashov <ever.zet@gmail.com>
21 */
22class MethodNode
23{
24    private $name;
25    private $code;
26    private $visibility = 'public';
27    private $static = false;
28    private $returnsReference = false;
29    private $returnType;
30    private $nullableReturnType = false;
31
32    /**
33     * @var ArgumentNode[]
34     */
35    private $arguments = array();
36
37    /**
38     * @var TypeHintReference
39     */
40    private $typeHintReference;
41
42    /**
43     * @param string $name
44     * @param string $code
45     */
46    public function __construct($name, $code = null, TypeHintReference $typeHintReference = null)
47    {
48        $this->name = $name;
49        $this->code = $code;
50        $this->typeHintReference = $typeHintReference ?: new TypeHintReference();
51    }
52
53    public function getVisibility()
54    {
55        return $this->visibility;
56    }
57
58    /**
59     * @param string $visibility
60     */
61    public function setVisibility($visibility)
62    {
63        $visibility = strtolower($visibility);
64
65        if (!in_array($visibility, array('public', 'private', 'protected'))) {
66            throw new InvalidArgumentException(sprintf(
67                '`%s` method visibility is not supported.', $visibility
68            ));
69        }
70
71        $this->visibility = $visibility;
72    }
73
74    public function isStatic()
75    {
76        return $this->static;
77    }
78
79    public function setStatic($static = true)
80    {
81        $this->static = (bool) $static;
82    }
83
84    public function returnsReference()
85    {
86        return $this->returnsReference;
87    }
88
89    public function setReturnsReference()
90    {
91        $this->returnsReference = true;
92    }
93
94    public function getName()
95    {
96        return $this->name;
97    }
98
99    public function addArgument(ArgumentNode $argument)
100    {
101        $this->arguments[] = $argument;
102    }
103
104    /**
105     * @return ArgumentNode[]
106     */
107    public function getArguments()
108    {
109        return $this->arguments;
110    }
111
112    public function hasReturnType()
113    {
114        return null !== $this->returnType;
115    }
116
117    /**
118     * @param string $type
119     */
120    public function setReturnType($type = null)
121    {
122        if ($type === '' || $type === null) {
123            $this->returnType = null;
124            return;
125        }
126        $typeMap = array(
127            'double' => 'float',
128            'real' => 'float',
129            'boolean' => 'bool',
130            'integer' => 'int',
131        );
132        if (isset($typeMap[$type])) {
133            $type = $typeMap[$type];
134        }
135        $this->returnType = $this->typeHintReference->isBuiltInReturnTypeHint($type) ?
136            $type :
137            '\\' . ltrim($type, '\\');
138    }
139
140    public function getReturnType()
141    {
142        return $this->returnType;
143    }
144
145    /**
146     * @param bool $bool
147     */
148    public function setNullableReturnType($bool = true)
149    {
150        $this->nullableReturnType = (bool) $bool;
151    }
152
153    /**
154     * @return bool
155     */
156    public function hasNullableReturnType()
157    {
158        return $this->nullableReturnType;
159    }
160
161    /**
162     * @param string $code
163     */
164    public function setCode($code)
165    {
166        $this->code = $code;
167    }
168
169    public function getCode()
170    {
171        if ($this->returnsReference)
172        {
173            return "throw new \Prophecy\Exception\Doubler\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), '{$this->name}');";
174        }
175
176        return (string) $this->code;
177    }
178
179    public function useParentCode()
180    {
181        $this->code = sprintf(
182            'return parent::%s(%s);', $this->getName(), implode(', ',
183                array_map(array($this, 'generateArgument'), $this->arguments)
184            )
185        );
186    }
187
188    private function generateArgument(ArgumentNode $arg)
189    {
190        $argument = '$'.$arg->getName();
191
192        if ($arg->isVariadic()) {
193            $argument = '...'.$argument;
194        }
195
196        return $argument;
197    }
198}
199