Builder::current()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 6
rs 10
1
<?php
2
/**
3
 * @author Gerard van Helden <[email protected]>
4
 * @copyright Zicht Online <http://zicht.nl>
5
 */
6
namespace Zicht\Bundle\FrameworkExtraBundle\Fixture;
7
8
use Zicht\Util\Str;
9
10
/**
11
 * Fixture builder helper class. Provides a fluent interface for building fixture objects in Doctrine ORM.
12
 */
13
class Builder
14
{
15
    /**
16
     * Creates a builder for the specified namespace
17
     *
18
     * @param string $namespaces
19
     * @return Builder
20
     */
21
    public static function create($namespaces)
22
    {
23
        return new self($namespaces);
24
    }
25
26
27
    /**
28
     * Constructor, initializes the builder object. To use the builder, call Builder::create(...)
29
     *
30
     * @param string $namespaces
31
     */
32
    private function __construct($namespaces)
33
    {
34
        $this->namespaces = (array)$namespaces;
0 ignored issues
show
Bug Best Practice introduced by
The property namespaces does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
35
        $this->stack    = array();
0 ignored issues
show
Bug Best Practice introduced by
The property stack does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
36
        $this->alwaysDo = array();
0 ignored issues
show
Bug Best Practice introduced by
The property alwaysDo does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
37
    }
38
39
40
    /**
41
     * Adds a method call to all fixture objects, typically array($manager, 'persist')
42
     *
43
     * @param callable $do
44
     * @return Builder
45
     */
46
    public function always($do)
47
    {
48
        $this->alwaysDo[]= $do;
0 ignored issues
show
Bug Best Practice introduced by
The property alwaysDo does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
49
        return $this;
50
    }
51
52
53
    /**
54
     * Implements the builder / fluent interface for building fixture objects.
55
     *
56
     * @param string $method
57
     * @param array $args
58
     * @return Builder
59
     *
60
     * @throws \BadMethodCallException
61
     */
62
    public function __call($method, $args)
63
    {
64
        if (method_exists($this->current(), $method)) {
65
            call_user_func_array(array($this->current(), $method), $args);
66
        } else {
67
            $entity = $method;
68
69
            $className = $this->resolve($entity);
70
            if ($className) {
71
                $class = new \ReflectionClass($className);
72
                if ($args) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $args of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
73
                    $entityInstance = $class->newInstanceArgs($args);
74
                } else {
75
                    $entityInstance = $class->newInstance();
76
                }
77
                $this->push($entityInstance);
78
            } else {
79
                throw new \BadMethodCallException(
80
                    "No class found for {$entity} in [" . join(", ", $this->namespaces) . "]"
81
                    . (
82
                        $this->current()
83
                            ? ", nor is it a method in " . get_class($this->current())
84
                            : ""
85
                    )
86
                );
87
            }
88
        }
89
        return $this;
90
    }
91
92
93
    /**
94
     * Resolve the entity name to any of the configured namespaces.
95
     * Returns null if not found.
96
     *
97
     * @param string $entity
98
     * @return null|string
99
     */
100
    private function resolve($entity)
101
    {
102
        foreach ($this->namespaces as $namespace) {
103
            $className = $namespace . '\\' . ucfirst($entity);
104
            if (class_exists($className)) {
105
                return $className;
106
            }
107
        }
108
        return null;
109
    }
110
111
112
    /**
113
     * Returns the top of the stack.
114
     *
115
     * @return mixed
116
     */
117
    protected function current()
118
    {
119
        if (count($this->stack)) {
120
            return $this->stack[count($this->stack) -1];
121
        }
122
        return null;
123
    }
124
125
126
    /**
127
     * Pushes an object onto the stack
128
     *
129
     * @param object $entity
130
     */
131
    protected function push($entity)
132
    {
133
        $this->stack[]= $entity;
0 ignored issues
show
Bug Best Practice introduced by
The property stack does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
134
    }
135
136
137
    /**
138
     * Returns one level up in the tree.
139
     *
140
     * @param null $setter
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $setter is correct as it would always require null to be passed?
Loading history...
141
     * @return Builder
142
     */
143
    public function end($setter = null)
144
    {
145
        if (!count($this->stack)) {
146
            throw new \UnexpectedValueException("Stack is empty. Did you call end() too many times?");
147
        }
148
        $current = array_pop($this->stack);
149
        if ($parent = $this->current()) {
150
            $parentClassName = get_class($parent);
151
            $entityLocalName = Str::classname(get_class($current));
152
153
            if ($current instanceof $parentClassName) {
154
                if (method_exists($parent, 'addChildren')) {
155
                    call_user_func(array($parent, 'addChildren'), $current);
156
                }
157
            }
158
            if (is_null($setter)) {
0 ignored issues
show
introduced by
The condition is_null($setter) is always true.
Loading history...
159
                foreach (array('set', 'add') as $methodPrefix) {
160
                    $methodName = $methodPrefix . $entityLocalName;
161
162
                    if (method_exists($parent, $methodName)) {
163
                        $setter = $methodName;
164
                        break;
165
                    }
166
                }
167
            }
168
            if (!is_null($setter)) {
169
                call_user_func(array($parent, $setter), $current);
170
            }
171
172
            $parentClassNames = array_merge(class_parents($parentClassName), array($parentClassName));
173
174
            foreach (array_reverse($parentClassNames) as $lParentClassName) {
175
                $lParentClass = Str::classname($lParentClassName);
176
                $parentSetter = 'set' . $lParentClass;
177
                if ($lParentClassName == get_class($current)) {
178
                    $parentSetter = 'setParent';
179
                }
180
                if (method_exists($current, $parentSetter)) {
181
                    call_user_func(
182
                        array($current, $parentSetter),
183
                        $parent
184
                    );
185
                    break;
186
                }
187
            }
188
            foreach ($this->alwaysDo as $callable) {
189
                call_user_func($callable, $parent);
190
            }
191
        }
192
        foreach ($this->alwaysDo as $callable) {
193
            call_user_func($callable, $current);
194
        }
195
        return $this;
196
    }
197
198
199
    /**
200
     * Returns the object that is currently the subject of building
201
     *
202
     * @return mixed
203
     * @throws \UnexpectedValueException
204
     */
205
    public function peek()
206
    {
207
        $c = count($this->stack);
208
        if ($c == 0) {
209
            throw new \UnexpectedValueException("The stack is empty. You should probably peek() before the last end() call.");
210
        }
211
        $ret = $this->stack[$c -1];
212
        return $ret;
213
    }
214
}
215