├── .gitignore
├── .php_cs
├── .scrutinizer.yml
├── .styleci.yml
├── .travis.yml
├── build
├── split-faster.sh
├── split-full.sh
└── split.sh
├── composer.json
├── contributing.md
├── license.txt
├── phpunit.xml
├── readme.md
├── src
└── Fist
│ ├── Container
│ ├── .gitignore
│ ├── .travis.yml
│ ├── composer.json
│ ├── license.txt
│ ├── phpunit.xml
│ ├── src
│ │ ├── BindingException.php
│ │ └── Container.php
│ └── tests
│ │ ├── ContainerTest.php
│ │ └── bootstrap.php
│ ├── Database
│ ├── .gitignore
│ ├── .travis.yml
│ ├── composer.json
│ ├── license.txt
│ ├── phpunit.xml
│ ├── readme.md
│ ├── src
│ │ ├── Connectors
│ │ │ ├── Connection.php
│ │ │ ├── ConnectionInterface.php
│ │ │ ├── MysqlConnection.php
│ │ │ └── SqliteConnection.php
│ │ ├── Database.php
│ │ ├── DatabaseException.php
│ │ └── Query
│ │ │ ├── Builder.php
│ │ │ ├── Grammars
│ │ │ ├── Grammar.php
│ │ │ ├── GrammarInterface.php
│ │ │ ├── MysqlGrammar.php
│ │ │ └── SqliteGrammar.php
│ │ │ └── Statement.php
│ └── tests
│ │ ├── DatabaseConnectionTest.php
│ │ └── bootstrap.php
│ ├── Facade
│ ├── .gitignore
│ ├── .travis.yml
│ ├── composer.json
│ ├── license.txt
│ ├── phpunit.xml
│ ├── src
│ │ ├── ContainerFacade.php
│ │ ├── ContainerFacadeInterface.php
│ │ ├── Facade.php
│ │ ├── FacadeInterface.php
│ │ └── InvalidArgumentException.php
│ └── tests
│ │ ├── FacadeTest.php
│ │ └── bootstrap.php
│ ├── Repository
│ ├── .gitignore
│ ├── .travis.yml
│ ├── composer.json
│ ├── license.txt
│ ├── phpunit.xml
│ ├── src
│ │ ├── ArrayRepository.php
│ │ ├── ContainerRepository.php
│ │ └── RepositoryInterface.php
│ └── tests
│ │ ├── RepositoryTest.php
│ │ └── bootstrap.php
│ ├── Routing
│ ├── .gitignore
│ ├── .travis.yml
│ ├── composer.json
│ ├── license.txt
│ ├── phpunit.xml
│ ├── src
│ │ ├── MethodNotAllowedException.php
│ │ ├── NotFoundException.php
│ │ ├── Route.php
│ │ └── Router.php
│ └── tests
│ │ ├── RoutingTest.php
│ │ └── bootstrap.php
│ └── Testing
│ ├── .gitignore
│ ├── .travis.yml
│ ├── composer.json
│ ├── license.txt
│ ├── phpunit.xml
│ ├── src
│ ├── TestCase.php
│ └── WithDatabase.php
│ └── tests
│ └── bootstrap.php
└── tests
└── bootstrap.php
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | composer.lock
3 | .subsplit/
4 | build/coverage.txt
5 | build/coverage/
6 | build/logs/
7 | build/report.junit.xml
8 | build/report.tap
9 |
--------------------------------------------------------------------------------
/.php_cs:
--------------------------------------------------------------------------------
1 | =7.0"
19 | },
20 | "replace": {
21 | "fist/container": "self.version",
22 | "fist/database": "self.version",
23 | "fist/repository": "self.version",
24 | "fist/routing": "self.version",
25 | "fist/facade": "self.version",
26 | "fist/testing": "self.version"
27 | },
28 | "require-dev": {
29 | "phpunit/phpunit": "~6.3",
30 | "codacy/coverage": "dev-master",
31 | "sllh/php-cs-fixer-styleci-bridge": "^2.1"
32 | },
33 | "autoload": {
34 | "psr-4": {
35 | "Fist\\Container\\": "src/Fist/Container/src/",
36 | "Fist\\Database\\": "src/Fist/Database/src/",
37 | "Fist\\Repository\\": "src/Fist/Repository/src/",
38 | "Fist\\Routing\\": "src/Fist/Routing/src/",
39 | "Fist\\Facade\\": "src/Fist/Facade/src/",
40 | "Fist\\Testing\\": "src/Fist/Testing/src/"
41 | }
42 | },
43 | "extra": {
44 | "branch-alias": {
45 | "dev-master": "1.0-dev"
46 | }
47 | },
48 | "minimum-stability": "dev"
49 | }
50 |
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | # Contribution Guide
2 |
3 | ## Bug Reports
4 |
5 | When you file a bug report, your issue should contain a title and a clear description of the issue. You should also include as much relevant information as possible and a code sample that demonstrates the issue. The goal of a bug report is to make it easy for yourself - and others - to replicate the bug and develop a fix.
6 |
7 | A bug report may also be sent in the form of a pull request containing a failing test.
8 |
9 | ## Branching
10 |
11 | All bug fixes should be sent to the latest stable branch. Bug fixes should never be sent to the master branch unless they fix features that exist only in the upcoming release.
12 |
13 | Minor features that are fully backwards compatible with the current Fistlab release may be sent to the latest stable branch.
14 |
15 | Major new features should always be sent to the master branch, which contains the upcoming Fistlab release.
16 |
17 | If you are unsure if your feature qualifies as a major or minor, please ask Mark Topper at mark@ulties.com.
18 |
19 | ## Coding Style
20 |
21 | Fistlab follows the PSR-2 coding standard and the PSR-4 autoloading standard.
22 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Fistlab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 | ./src/Fist/*/tests
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | ./src/Fist/*/src
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # The Fistlab PHP Components
2 |
3 | [](https://styleci.io/repos/67337527)
4 | [](https://travis-ci.org/fistlab/php)
5 | [](https://packagist.org/packages/fistlab/php)
6 | [](https://packagist.org/packages/fistlab/php)
7 | [](https://packagist.org/packages/fistlab/php)
8 | [](https://packagist.org/packages/fistlab/php)
9 | [](https://codeclimate.com/github/fistlab/php)
10 | [](https://codeclimate.com/github/fistlab/php/coverage)
11 | [](https://scrutinizer-ci.com/g/fistlab/php/?branch=master)
12 | [](https://scrutinizer-ci.com/g/fistlab/php/?branch=master)
13 | [](https://scrutinizer-ci.com/g/fistlab/php/build-status/master)
14 | [](https://www.codacy.com/app/marktopper/php?utm_source=github.com&utm_medium=referral&utm_content=fistlab/php&utm_campaign=Badge_Grade)
15 | [](https://codecov.io/gh/fistlab/php)
16 | [](https://www.codetriage.com/fistlab/php)
17 |
18 | ## Idea
19 |
20 | The idea is to create a set of components in various programming languages, which however works almost the same way. This way it will be easier for developers to go around and play with another programming language without having to find a whole new set of components and dig into how it works.
21 | Current programming languages: [php](https://github.com/fistlab/php)
22 |
23 | ## Components
24 |
25 | * [Container](https://github.com/fistphp/container)
26 | * [Database](https://github.com/fistphp/database)
27 | * [Repository](https://github.com/fistphp/repository)
28 |
29 | ## Contributing
30 |
31 | Thank you for considering contributing to the Fistlab components! The contribution guide can be found [here](https://github.com/fistlab/php/blob/master/contributing.md).
32 |
33 | ## Security Vulnerabilities
34 |
35 | If you discover a security vulnerability within the components, please send an e-mail to Mark Topper at mark@ulties.com. All security vulnerabilities will be promptly addressed.
36 |
37 | ## License
38 |
39 | The Fistlab Components is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT).
40 |
--------------------------------------------------------------------------------
/src/Fist/Container/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | composer.lock
3 | .subsplit/
4 |
--------------------------------------------------------------------------------
/src/Fist/Container/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.6
5 | - 7.0
6 | - 7.1
7 |
8 | env:
9 | global:
10 | - setup=basic
11 |
12 | matrix:
13 | include:
14 | - php: 5.6
15 | env: setup=lowest
16 | - php: 5.6
17 | env: setup=stable
18 |
19 | sudo: false
20 |
21 | before_install:
22 | - if [[ $TRAVIS_PHP_VERSION != 7.1 ]] ; then phpenv config-rm xdebug.ini; fi
23 | - travis_retry composer self-update
24 |
25 | install:
26 | - if [[ $setup = 'basic' ]]; then travis_retry composer install --no-interaction --prefer-dist --no-suggest; fi
27 | - if [[ $setup = 'stable' ]]; then travis_retry composer update --prefer-dist --no-interaction --prefer-stable --no-suggest; fi
28 | - if [[ $setup = 'lowest' ]]; then travis_retry composer update --prefer-dist --no-interaction --prefer-lowest --prefer-stable --no-suggest; fi
29 |
30 | script: vendor/bin/phpunit
31 |
32 | matrix:
33 | fast_finish: true
34 |
--------------------------------------------------------------------------------
/src/Fist/Container/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fist/container",
3 | "description": "The Fistlab PHP Container Components",
4 | "keywords": ["fistlab", "fistphp", "component", "container"],
5 | "license": "MIT",
6 | "homepage": "https://github.com/fistphp/container",
7 | "support": {
8 | "issues": "https://github.com/fistlab/php/issues",
9 | "source": "https://github.com/fistlab/php"
10 | },
11 | "authors": [
12 | {
13 | "name": "Mark Topper",
14 | "email": "mark@ulties.com"
15 | }
16 | ],
17 | "require": {
18 | "php": ">=5.6.4"
19 | },
20 | "require-dev": {
21 | "phpunit/phpunit": "~5.4",
22 | "fist/testing": "self.version"
23 | },
24 | "autoload": {
25 | "psr-4": {
26 | "Fist\\Container\\": "src/"
27 | }
28 | },
29 | "extra": {
30 | "branch-alias": {
31 | "dev-master": "1.0-dev"
32 | }
33 | },
34 | "minimum-stability": "dev"
35 | }
36 |
--------------------------------------------------------------------------------
/src/Fist/Container/license.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Fistlab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Fist/Container/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 | ./tests
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Fist/Container/src/BindingException.php:
--------------------------------------------------------------------------------
1 | resolvingListeners[$name])) {
48 | $this->resolvingListeners[$name] = [];
49 | }
50 |
51 | $this->resolvingListeners[$name][] = $closure;
52 | }
53 |
54 | public function bind($name, $closure, $shared = false)
55 | {
56 | $name = $this->normalize($name);
57 |
58 | $bound = $this->bound($name);
59 |
60 | $this->bindings[$name] = [
61 | 'concrete' => $closure,
62 | 'shared' => $shared,
63 | ];
64 |
65 | if ($bound) {
66 | $this->rebound($name);
67 | }
68 | }
69 |
70 | protected function getCallReflector($callback)
71 | {
72 | if (is_string($callback) && strpos($callback, '::') !== false) {
73 | $callback = explode('::', $callback);
74 | }
75 |
76 | if (is_array($callback)) {
77 | return new ReflectionMethod($callback[0], $callback[1]);
78 | }
79 |
80 | return new ReflectionFunction($callback);
81 | }
82 |
83 | protected function addDependencyForCallParameter(ReflectionParameter $parameter, array &$parameters, &$dependencies)
84 | {
85 | if (array_key_exists($parameter->name, $parameters)) {
86 | $dependencies[] = $parameters[$parameter->name];
87 |
88 | unset($parameters[$parameter->name]);
89 | } elseif ($parameter->getClass()) {
90 | $dependencies[] = $this->make($parameter->getClass()->name);
91 | } elseif ($parameter->isDefaultValueAvailable()) {
92 | $dependencies[] = $parameter->getDefaultValue();
93 | }
94 | }
95 |
96 | protected function getMethodDependencies($callback, array $parameters = [])
97 | {
98 | $dependencies = [];
99 |
100 | foreach ($this->getCallReflector($callback)->getParameters() as $parameter) {
101 | $this->addDependencyForCallParameter($parameter, $parameters, $dependencies);
102 | }
103 |
104 | return array_merge($dependencies, $parameters);
105 | }
106 |
107 | protected function isCallableWithAtSign($callback)
108 | {
109 | return is_string($callback) && strpos($callback, '@') !== false;
110 | }
111 |
112 | protected function callClass($target, array $parameters = [], $defaultMethod = null)
113 | {
114 | $segments = explode('@', $target);
115 |
116 | $method = count($segments) == 2 ? $segments[1] : $defaultMethod;
117 |
118 | if (is_null($method)) {
119 | throw new InvalidArgumentException('Method not provided.');
120 | }
121 |
122 | return $this->call([$this->make($segments[0]), $method], $parameters);
123 | }
124 |
125 | public function call($callback, array $parameters = [], $defaultMethod = null)
126 | {
127 | if ($this->isCallableWithAtSign($callback) || $defaultMethod) {
128 | return $this->callClass($callback, $parameters, $defaultMethod);
129 | }
130 |
131 | $dependencies = $this->getMethodDependencies($callback, $parameters);
132 |
133 | return call_user_func_array($callback, $dependencies);
134 | }
135 |
136 | public function instance($name, $instance)
137 | {
138 | $name = $this->normalize($name);
139 |
140 | $bound = $this->bound($name);
141 |
142 | $this->instances[$name] = $instance;
143 |
144 | if ($bound) {
145 | $this->rebound($name);
146 | }
147 | }
148 |
149 | protected function rebound($name)
150 | {
151 | if (isset($this->rebindingListeners[$name])) {
152 | foreach ($this->rebindingListeners[$name] as $listener) {
153 | $listener();
154 | }
155 | }
156 | }
157 |
158 | public function rebinding($name, Closure $closure)
159 | {
160 | $name = $this->normalize($name);
161 |
162 | if (! isset($this->rebindingListeners[$name])) {
163 | $this->rebindingListeners[$name] = [];
164 | }
165 |
166 | $this->rebindingListeners[$name][] = $closure;
167 | }
168 |
169 | public function contextual($name, $needs, $give)
170 | {
171 | $name = $this->normalize($name);
172 | $needs = $this->normalize($needs);
173 | $give = $this->normalize($give);
174 |
175 | if (! isset($this->contextual[$name])) {
176 | $this->contextual[$name] = [];
177 | }
178 |
179 | $this->contextual[$name][$needs] = $give;
180 | }
181 |
182 | protected function getContextualConcrete($abstract)
183 | {
184 | $build = end($this->buildStack);
185 |
186 | if (isset($this->contextual[$build][$abstract])) {
187 | return $this->contextual[$build][$abstract];
188 | }
189 | }
190 |
191 | protected function keyParametersByArgument(array $dependencies, array $parameters)
192 | {
193 | foreach ($parameters as $key => $value) {
194 | if (is_numeric($key)) {
195 | unset($parameters[$key]);
196 |
197 | $parameters[$dependencies[$key]->name] = $value;
198 | }
199 | }
200 |
201 | return $parameters;
202 | }
203 |
204 | protected function getConcrete($name)
205 | {
206 | if (! is_null($concrete = $this->getContextualConcrete($name))) {
207 | return $concrete;
208 | }
209 |
210 | if (isset($this->bindings[$name])) {
211 | return $this->bindings[$name]['concrete'];
212 | }
213 |
214 | return $name;
215 | }
216 |
217 | public function make($name, array $parameters = [])
218 | {
219 | $name = $this->normalize($name);
220 |
221 | if (isset($this->instances[$name])) {
222 | return $this->instances[$name];
223 | }
224 |
225 | $concrete = $this->getConcrete($name);
226 |
227 | if ($this->isBuildable($concrete, $name)) {
228 | $object = $this->build($concrete, $parameters);
229 | } else {
230 | $object = $this->make($concrete, $parameters);
231 | }
232 |
233 | foreach ($this->getExtenders($name) as $extender) {
234 | $object = $extender($object, $this);
235 | }
236 |
237 | if ($this->isShared($name)) {
238 | $this->instances[$name] = $object;
239 | }
240 |
241 | $this->fireResolvingCallbacks($name, $object);
242 |
243 | return $object;
244 | }
245 |
246 | protected function fireResolvingCallbacks($name, $object)
247 | {
248 | if (isset($this->resolvingListeners[$name])) {
249 | foreach ($this->resolvingListeners[$name] as $listener) {
250 | $listener($object);
251 | }
252 | }
253 | }
254 |
255 | public function extend($name, Closure $closure)
256 | {
257 | $name = $this->normalize($name);
258 |
259 | if (isset($this->instances[$name])) {
260 | $this->instances[$name] = $closure($this->instances[$name], $this);
261 |
262 | $this->rebound($name);
263 | } else {
264 | $this->extenders[$name][] = $closure;
265 | }
266 | }
267 |
268 | protected function isBuildable($concrete, $abstract)
269 | {
270 | return $concrete === $abstract || $concrete instanceof Closure;
271 | }
272 |
273 | public function build($concrete, array $parameters = [])
274 | {
275 | if ($concrete instanceof Closure) {
276 | return $concrete($this, $parameters);
277 | }
278 |
279 | $reflector = new ReflectionClass($concrete);
280 |
281 | if (! $reflector->isInstantiable()) {
282 | if (! empty($this->buildStack)) {
283 | $previous = implode(', ', $this->buildStack);
284 |
285 | $message = "Target [$concrete] is not instantiable while building [$previous].";
286 | } else {
287 | $message = "Target [$concrete] is not instantiable.";
288 | }
289 |
290 | throw new BindingException($message);
291 | }
292 |
293 | $this->buildStack[] = $concrete;
294 |
295 | $constructor = $reflector->getConstructor();
296 |
297 | if (is_null($constructor)) {
298 | array_pop($this->buildStack);
299 |
300 | return new $concrete();
301 | }
302 |
303 | $dependencies = $constructor->getParameters();
304 |
305 | $parameters = $this->keyParametersByArgument(
306 | $dependencies, $parameters
307 | );
308 |
309 | $instances = $this->getDependencies(
310 | $dependencies, $parameters
311 | );
312 |
313 | array_pop($this->buildStack);
314 |
315 | return $reflector->newInstanceArgs($instances);
316 | }
317 |
318 | protected function getDependencies(array $parameters, array $primitives = [])
319 | {
320 | $dependencies = [];
321 |
322 | foreach ($parameters as $parameter) {
323 | $dependency = $parameter->getClass();
324 |
325 | if (array_key_exists($parameter->name, $primitives)) {
326 | $dependencies[] = $primitives[$parameter->name];
327 | } elseif (is_null($dependency)) {
328 | $dependencies[] = $this->resolveNonClass($parameter);
329 | } else {
330 | $dependencies[] = $this->resolveClass($parameter);
331 | }
332 | }
333 |
334 | return $dependencies;
335 | }
336 |
337 | protected function resolveNonClass(ReflectionParameter $parameter)
338 | {
339 | if (! is_null($concrete = $this->getContextualConcrete('$'.$parameter->name))) {
340 | if ($concrete instanceof Closure) {
341 | return call_user_func($concrete, $this);
342 | } else {
343 | return $concrete;
344 | }
345 | }
346 |
347 | if ($parameter->isDefaultValueAvailable()) {
348 | return $parameter->getDefaultValue();
349 | }
350 |
351 | $message = "Unresolvable dependency resolving [$parameter] in class {$parameter->getDeclaringClass()->name}";
352 |
353 | throw new BindingException($message);
354 | }
355 |
356 | public function getExtenders($name)
357 | {
358 | return isset($this->extenders[$name]) ? $this->extenders[$name] : [];
359 | }
360 |
361 | protected function resolveClass(ReflectionParameter $parameter)
362 | {
363 | try {
364 | return $this->make($parameter->getClass()->name);
365 | } catch (BindingException $e) {
366 | if ($parameter->isOptional()) {
367 | return $parameter->getDefaultValue();
368 | }
369 |
370 | throw $e;
371 | }
372 | }
373 |
374 | public function isShared($name)
375 | {
376 | return isset($this->bindings[$name]) && $this->bindings[$name]['shared'];
377 | }
378 |
379 | public function bound($name)
380 | {
381 | $name = $this->normalize($name);
382 |
383 | return isset($this->bindings[$name]) || isset($this->instances[$name]);
384 | }
385 |
386 | public function singleton($name, Closure $closure)
387 | {
388 | $this->bind($name, $closure, true);
389 | }
390 |
391 | public function normalize($name)
392 | {
393 | return is_string($name) ? ltrim($name, '\\') : $name;
394 | }
395 | }
396 |
--------------------------------------------------------------------------------
/src/Fist/Container/tests/ContainerTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(Container::class, $container);
13 | }
14 |
15 | public function testInstancing()
16 | {
17 | $application = new Container();
18 |
19 | Container::setInstance($application);
20 |
21 | $this->assertInstanceOf(Container::class, Container::getInstance());
22 | $this->assertSame($application, Container::getInstance());
23 | }
24 |
25 | public function testAutoInstancing()
26 | {
27 | Container::removeInstance();
28 |
29 | $this->assertSame(null, Container::getInstance());
30 |
31 | $application = new Container();
32 |
33 | $this->assertInstanceOf(Container::class, Container::getInstance());
34 | $this->assertSame($application, Container::getInstance());
35 | }
36 |
37 | public function testBinding()
38 | {
39 | $container = new Container();
40 |
41 | $container->bind('foo', function () {
42 | return 'bar';
43 | });
44 |
45 | $this->assertEquals('bar', $container->make('foo'));
46 |
47 | $container->bind(stdClass::class, function () {
48 | return new stdClass();
49 | });
50 |
51 | $this->assertNotSame(
52 | $container->make(stdClass::class),
53 | $container->make(stdClass::class)
54 | );
55 | }
56 |
57 | public function testBound()
58 | {
59 | $container = new Container();
60 |
61 | $this->assertFalse($container->bound('foo'));
62 |
63 | $container->bind('foo', function () {
64 | return 'bar';
65 | });
66 |
67 | $this->assertTrue($container->bound('foo'));
68 | }
69 |
70 | public function testBindingShared()
71 | {
72 | $container = new Container();
73 |
74 | $class = new stdClass();
75 |
76 | $container->singleton('class', function () use ($class) {
77 | return $class;
78 | });
79 |
80 | $this->assertSame($class, $container->make('class'));
81 | }
82 |
83 | public function testSlashesAreHandled()
84 | {
85 | $container = new Container();
86 |
87 | $container->bind('\Foo', function () {
88 | return 'bar';
89 | });
90 |
91 | $this->assertEquals('bar', $container->make('Foo'));
92 | }
93 |
94 | public function testParametersCanOverrideDependencies()
95 | {
96 | $container = new Container();
97 |
98 | $stub = new ContainerDependentTestStub(
99 | $mock = $this->createMock(ContainerStubInterface::class)
100 | );
101 |
102 | $resolved = $container->make(ContainerNestedDependentTestStub::class, [$stub]);
103 |
104 | $this->assertInstanceOf(ContainerNestedDependentTestStub::class, $resolved);
105 |
106 | $this->assertEquals($mock, $resolved->stub->implementation);
107 | }
108 |
109 | public function testContainerIsPassed()
110 | {
111 | $container = new Container();
112 |
113 | $container->bind('container', function (Container $container) {
114 | return $container;
115 | });
116 |
117 | $this->assertSame(
118 | $container,
119 | $container->make('container')
120 | );
121 | }
122 |
123 | public function testOverrideBindings()
124 | {
125 | $container = new Container();
126 |
127 | $container->bind('foo', function () {
128 | return 'bar';
129 | });
130 |
131 | $this->assertEquals('bar', $container->make('foo'));
132 |
133 | $container->bind('foo', function () {
134 | return 'baz';
135 | });
136 |
137 | $this->assertEquals('baz', $container->make('foo'));
138 | }
139 |
140 | public function testExtendedBindings()
141 | {
142 | $container = new Container();
143 |
144 | $container->bind('foo', function () {
145 | return 'foo';
146 | });
147 |
148 | $container->extend('foo', function ($old) {
149 | return $old.'bar';
150 | });
151 |
152 | $container->extend('foo', function ($old) {
153 | return $old.'baz';
154 | });
155 |
156 | $this->assertEquals('foobarbaz', $container->make('foo'));
157 | }
158 |
159 | public function testExtendIsLazyInitialized()
160 | {
161 | $container = new Container();
162 |
163 | $container->extend(ContainerLazyExtendTestStub::class, function (ContainerLazyExtendTestStub $object) {
164 | $object->init();
165 |
166 | return $object;
167 | });
168 |
169 | $this->assertFalse(ContainerLazyExtendTestStub::$initialized);
170 |
171 | $container->make(ContainerLazyExtendTestStub::class);
172 |
173 | $this->assertTrue(ContainerLazyExtendTestStub::$initialized);
174 | }
175 |
176 | public function testExtendCanBeCalledBeforeBind()
177 | {
178 | $container = new Container();
179 |
180 | $container->extend('foo', function ($old) {
181 | return $old.'bar';
182 | });
183 |
184 | $container->bind('foo', function () {
185 | return 'foo';
186 | });
187 |
188 | $this->assertEquals('foobar', $container->make('foo'));
189 | }
190 |
191 | public function testParametersCanBePassedThroughToClosure()
192 | {
193 | $container = new Container();
194 |
195 | $container->bind('foo', function (Container $container, $parameters) {
196 | return $parameters;
197 | });
198 |
199 | $this->assertEquals([1, 2, 3], $container->make('foo', [1, 2, 3]));
200 | }
201 |
202 | public function testResolutionOfDefaultParameters()
203 | {
204 | $container = new Container();
205 |
206 | $instance = $container->make(ContainerDefaultValueTestStub::class);
207 |
208 | $this->assertInstanceOf(ContainerTestStub::class, $instance->stub);
209 |
210 | $this->assertEquals('mark', $instance->name);
211 | }
212 |
213 | public function testResolvingCallbacksAreCalled()
214 | {
215 | $container = new Container();
216 |
217 | $container->resolving('foo', function ($object) {
218 | return $object->name = 'mark';
219 | });
220 |
221 | $container->bind('foo', function () {
222 | return new stdClass();
223 | });
224 |
225 | $instance = $container->make('foo');
226 |
227 | $this->assertEquals('mark', $instance->name);
228 | }
229 |
230 | public function testReboundListeners()
231 | {
232 | $test = new stdClass();
233 | $test->rebound = false;
234 |
235 | $container = new Container();
236 |
237 | $container->bind('foo', function () {
238 | });
239 |
240 | $container->rebinding('foo', function () use ($test) {
241 | $test->rebound = true;
242 | });
243 |
244 | $this->assertFalse($test->rebound);
245 |
246 | $container->bind('foo', function () {
247 | });
248 |
249 | $this->assertTrue($test->rebound);
250 | }
251 |
252 | public function testReboundListenersOnInstances()
253 | {
254 | $test = new stdClass();
255 | $test->rebound = false;
256 |
257 | $container = new Container();
258 |
259 | $container->instance('foo', function () {
260 | });
261 |
262 | $container->rebinding('foo', function () use ($test) {
263 | $test->rebound = true;
264 | });
265 |
266 | $container->instance('foo', function () {
267 | });
268 |
269 | $this->assertTrue($test->rebound);
270 | }
271 |
272 | public function testPassingSomePrimitiveParameters()
273 | {
274 | $container = new Container();
275 |
276 | $value = $container->make(ContainerMixedPrimitiveTestStub::class, [
277 | 'first' => 'mark',
278 | 'last' => 'topper',
279 | ]);
280 |
281 | $this->assertInstanceOf(ContainerMixedPrimitiveTestStub::class, $value);
282 |
283 | $this->assertEquals('mark', $value->first);
284 |
285 | $this->assertEquals('topper', $value->last);
286 |
287 | $this->assertInstanceOf(ContainerTestStub::class, $value->stub);
288 |
289 | $container = new Container();
290 |
291 | $value = $container->make(ContainerMixedPrimitiveTestStub::class, [
292 | 0 => 'mark',
293 | 2 => 'topper',
294 | ]);
295 |
296 | $this->assertInstanceOf(ContainerMixedPrimitiveTestStub::class, $value);
297 |
298 | $this->assertEquals('mark', $value->first);
299 |
300 | $this->assertEquals('topper', $value->last);
301 |
302 | $this->assertInstanceOf(ContainerTestStub::class, $value->stub);
303 | }
304 |
305 | public function testCreatingBoundConcreteClassPassesParameters()
306 | {
307 | $container = new Container();
308 |
309 | $container->bind('TestAbstractClass', ContainerConstructorParameterLoggingTestStub::class);
310 |
311 | $parameters = ['foo', 'bar'];
312 |
313 | $instance = $container->make('TestAbstractClass', $parameters);
314 |
315 | $this->assertInstanceOf(ContainerConstructorParameterLoggingTestStub::class, $instance);
316 |
317 | $this->assertEquals($parameters, $instance->receivedParameters);
318 | }
319 |
320 | /**
321 | * @expectedException Fist\Container\BindingException
322 | * @expectedExceptionMessage Unresolvable dependency resolving [Parameter #0 [ $first ]] in class ContainerMixedPrimitiveTestStub
323 | */
324 | public function testInternalClassWithDefaultParameters()
325 | {
326 | $container = new Container();
327 |
328 | $container->make(ContainerMixedPrimitiveTestStub::class, []);
329 | }
330 |
331 | /**
332 | * @expectedException Fist\Container\BindingException
333 | * @expectedExceptionMessage Target [ContainerStubInterface] is not instantiable.
334 | */
335 | public function testBindingResolutionExceptionMessage()
336 | {
337 | $container = new Container();
338 |
339 | $container->make(ContainerStubInterface::class, []);
340 | }
341 |
342 | /**
343 | * @expectedException Fist\Container\BindingException
344 | * @expectedExceptionMessage Target [ContainerStubInterface] is not instantiable while building [ContainerTestContextInjectOne].
345 | */
346 | public function testBindingResolutionExceptionMessageIncludesBuildStack()
347 | {
348 | $container = new Container();
349 |
350 | $container->make(ContainerTestContextInjectOne::class, []);
351 | }
352 |
353 | public function testCallWithDependencies()
354 | {
355 | $container = new Container();
356 |
357 | $result = $container->call(function (stdClass $foo, $bar = []) {
358 | return func_get_args();
359 | });
360 |
361 | $this->assertInstanceOf(stdClass::class, $result[0]);
362 |
363 | $this->assertEquals([], $result[1]);
364 |
365 | $result = $container->call(function (stdClass $foo, $bar = []) {
366 | return func_get_args();
367 | }, ['bar' => 'mark']);
368 |
369 | $this->assertInstanceOf(stdClass::class, $result[0]);
370 |
371 | $this->assertEquals('mark', $result[1]);
372 | }
373 |
374 | /**
375 | * @expectedException ReflectionException
376 | */
377 | public function testCallWithAtSignBasedClassReferencesWithoutMethodThrowsException()
378 | {
379 | $container = new Container();
380 |
381 | $container->call(ContainerTestCallStub::class);
382 | }
383 |
384 | public function testCallWithAtSignBasedClassReferences()
385 | {
386 | $container = new Container();
387 |
388 | $result = $container->call('ContainerTestCallStub@work', ['foo', 'bar']);
389 |
390 | $this->assertEquals(['foo', 'bar'], $result);
391 |
392 | $container = new Container();
393 |
394 | $result = $container->call('ContainerTestCallStub@inject');
395 |
396 | $this->assertInstanceOf(ContainerTestStub::class, $result[0]);
397 |
398 | $this->assertEquals('mark', $result[1]);
399 |
400 | $container = new Container();
401 |
402 | $result = $container->call('ContainerTestCallStub@inject', ['default' => 'foo']);
403 |
404 | $this->assertInstanceOf(ContainerTestStub::class, $result[0]);
405 |
406 | $this->assertEquals('foo', $result[1]);
407 |
408 | $container = new Container();
409 |
410 | $result = $container->call(ContainerTestCallStub::class, ['foo', 'bar'], 'work');
411 |
412 | $this->assertEquals(['foo', 'bar'], $result);
413 | }
414 |
415 | public function testCallWithCallableArray()
416 | {
417 | $container = new Container();
418 |
419 | $stub = new ContainerTestCallStub();
420 |
421 | $result = $container->call([$stub, 'work'], ['foo', 'bar']);
422 |
423 | $this->assertEquals(['foo', 'bar'], $result);
424 | }
425 |
426 | public function testCallWithStaticMethodNameString()
427 | {
428 | $container = new Container();
429 |
430 | $result = $container->call('ContainerStaticMethodStub::inject');
431 |
432 | $this->assertInstanceOf(ContainerTestStub::class, $result[0]);
433 |
434 | $this->assertEquals('mark', $result[1]);
435 | }
436 |
437 | public function testCallWithGlobalMethodName()
438 | {
439 | $container = new Container();
440 |
441 | $result = $container->call('containerInjectTest');
442 |
443 | $this->assertInstanceOf(ContainerTestStub::class, $result[0]);
444 |
445 | $this->assertEquals('mark', $result[1]);
446 | }
447 |
448 | public function testContainerCanInjectDifferentImplementationsDependingOnContext()
449 | {
450 | $container = new Container();
451 |
452 | $container->bind(ContainerStubInterface::class, ContainerImplementationTestStub::class);
453 |
454 | $container->contextual(ContainerTestContextInjectOne::class, ContainerStubInterface::class, ContainerImplementationTestStub::class);
455 | $container->contextual(ContainerTestContextInjectTwo::class, ContainerStubInterface::class, ContainerImplementationTestStubTwo::class);
456 |
457 | $one = $container->make(ContainerTestContextInjectOne::class);
458 | $two = $container->make(ContainerTestContextInjectTwo::class);
459 |
460 | $this->assertInstanceOf(ContainerImplementationTestStub::class, $one->impl);
461 | $this->assertInstanceOf(ContainerImplementationTestStubTwo::class, $two->impl);
462 | }
463 |
464 | public function testContainerCanInjectDifferentImplementationsDependingOnContextWithClosure()
465 | {
466 | $container = new Container();
467 |
468 | $container->bind(ContainerStubInterface::class, ContainerImplementationTestStub::class);
469 |
470 | $container->contextual(ContainerTestContextInjectOne::class, ContainerStubInterface::class, ContainerImplementationTestStub::class);
471 | $container->contextual(ContainerTestContextInjectTwo::class, ContainerStubInterface::class, function (Container $container) {
472 | return $container->make(ContainerImplementationTestStubTwo::class);
473 | });
474 |
475 | $one = $container->make(ContainerTestContextInjectOne::class);
476 | $two = $container->make(ContainerTestContextInjectTwo::class);
477 |
478 | $this->assertInstanceOf(ContainerImplementationTestStub::class, $one->impl);
479 | $this->assertInstanceOf(ContainerImplementationTestStubTwo::class, $two->impl);
480 | }
481 |
482 | public function testContainerCanInjectSimpleVariable()
483 | {
484 | $container = new Container();
485 |
486 | $container->contextual(ContainerInjectVariableStub::class, '$something', 9000);
487 |
488 | $instance = $container->make('ContainerInjectVariableStub');
489 |
490 | $this->assertEquals(9000, $instance->something);
491 |
492 | $container = new Container();
493 |
494 | $container->contextual('ContainerInjectVariableStub', '$something', function (Container $container) {
495 | return $container->make('ContainerTestStub');
496 | });
497 |
498 | $instance = $container->make('ContainerInjectVariableStub');
499 |
500 | $this->assertInstanceOf('ContainerTestStub', $instance->something);
501 | }
502 | }
503 |
504 | interface ContainerStubInterface
505 | {
506 | //
507 | }
508 |
509 | class ContainerTestStub
510 | {
511 | //
512 | }
513 |
514 | class ContainerImplementationTestStub implements ContainerStubInterface
515 | {
516 | //
517 | }
518 |
519 | class ContainerImplementationTestStubTwo implements ContainerStubInterface
520 | {
521 | //
522 | }
523 |
524 | class ContainerDependentTestStub
525 | {
526 | public $implementation;
527 |
528 | public function __construct(ContainerStubInterface $implementation)
529 | {
530 | $this->implementation = $implementation;
531 | }
532 | }
533 |
534 | class ContainerNestedDependentTestStub
535 | {
536 | public $stub;
537 |
538 | public function __construct(ContainerDependentTestStub $stub)
539 | {
540 | $this->stub = $stub;
541 | }
542 | }
543 |
544 | class ContainerDefaultValueTestStub
545 | {
546 | public $stub;
547 |
548 | public $name;
549 |
550 | public function __construct(ContainerTestStub $stub, $name = 'mark')
551 | {
552 | $this->stub = $stub;
553 |
554 | $this->name = $name;
555 | }
556 | }
557 |
558 | class ContainerMixedPrimitiveTestStub
559 | {
560 | public $first;
561 |
562 | public $last;
563 |
564 | public $stub;
565 |
566 | public function __construct($first, ContainerTestStub $stub, $last = null)
567 | {
568 | $this->stub = $stub;
569 |
570 | $this->last = $last;
571 |
572 | $this->first = $first;
573 | }
574 | }
575 |
576 | class ContainerConstructorParameterLoggingTestStub
577 | {
578 | public $receivedParameters;
579 |
580 | public function __construct($first, $second)
581 | {
582 | $this->receivedParameters = func_get_args();
583 | }
584 | }
585 |
586 | class ContainerLazyExtendTestStub
587 | {
588 | public static $initialized = false;
589 |
590 | public function init()
591 | {
592 | static::$initialized = true;
593 | }
594 | }
595 |
596 | class ContainerTestCallStub
597 | {
598 | public function work()
599 | {
600 | return func_get_args();
601 | }
602 |
603 | public function inject(ContainerTestStub $stub, $default = 'mark')
604 | {
605 | return func_get_args();
606 | }
607 | }
608 |
609 | class ContainerTestContextInjectOne
610 | {
611 | public $impl;
612 |
613 | public function __construct(ContainerStubInterface $impl)
614 | {
615 | $this->impl = $impl;
616 | }
617 | }
618 |
619 | class ContainerTestContextInjectTwo
620 | {
621 | public $impl;
622 |
623 | public function __construct(ContainerStubInterface $impl)
624 | {
625 | $this->impl = $impl;
626 | }
627 | }
628 |
629 | class ContainerStaticMethodStub
630 | {
631 | public static function inject(ContainerTestStub $stub, $default = 'mark')
632 | {
633 | return func_get_args();
634 | }
635 | }
636 |
637 | class ContainerInjectVariableStub
638 | {
639 | public $concrete;
640 | public $something;
641 |
642 | public function __construct(ContainerTestStub $concrete, $something)
643 | {
644 | $this->concrete = $concrete;
645 | $this->something = $something;
646 | }
647 | }
648 |
649 | function containerInjectTest(ContainerTestStub $stub, $default = 'mark')
650 | {
651 | return func_get_args();
652 | }
653 |
--------------------------------------------------------------------------------
/src/Fist/Container/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | =5.6.4",
19 | "fist/repository": "self.version"
20 | },
21 | "require-dev": {
22 | "phpunit/phpunit": "~5.4",
23 | "fist/testing": "self.version",
24 | "fist/container": "self.version"
25 | },
26 | "autoload": {
27 | "psr-4": {
28 | "Fist\\Database\\": "src/"
29 | }
30 | },
31 | "extra": {
32 | "branch-alias": {
33 | "dev-master": "1.0-dev"
34 | }
35 | },
36 | "minimum-stability": "dev"
37 | }
38 |
--------------------------------------------------------------------------------
/src/Fist/Database/license.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Fistlab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Fist/Database/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 | ./tests
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Fist/Database/readme.md:
--------------------------------------------------------------------------------
1 | # Fistlab Database
2 |
3 | [](https://styleci.io/repos/67337527)
4 | [](https://travis-ci.org/fistphp/database)
5 | [](https://packagist.org/packages/fist/database)
6 | [](https://packagist.org/packages/fist/database)
7 | [](https://packagist.org/packages/fist/database)
8 | [](https://packagist.org/packages/fist/database)
9 |
10 | The Fistlab Database component is a database toolkit, providing an expressive query builder. It currently supports MySQL and SQLite.
11 |
12 | Languages: __php__.
13 |
14 | ## Installation
15 |
16 | Install using Composer.
17 | ```
18 | composer require fist/database
19 | ```
20 |
21 | ## Preparing
22 |
23 | The constructor accepts an instance of [`RepositoryInterface`](https://github.com/fistphp/repository/blob/master/RepositoryInterface.php) from [`fist/repository`](https://github.com/fistphp/repository).
24 |
25 | Example
26 |
27 | ```
28 | $db = new \Fist\Database\Database(
29 | $repository = new Fist\Repository\ArrayRepository([
30 | 'default' => [
31 | 'connection' => 'default',
32 | 'driver' => 'mysql',
33 | ],
34 | 'connections' => [
35 | 'default' => [
36 | 'driver' => 'mysql',
37 | 'hostname' => '127.0.0.1',
38 | 'database' => 'database',
39 | 'username' => 'root',
40 | 'password' => '',
41 | ],
42 | ],
43 | 'drivers' => [
44 | 'mysql' => \Fist\Database\Connectors\MysqlConnection::class,
45 | ],
46 | ])
47 | );
48 | ```
49 |
50 | > I have made more setup at this [gist](https://gist.github.com/marktopper/2f783901f19da6597b935e3b432fa41d).
51 |
52 | ## Usage
53 |
54 | #### Running raw statements
55 |
56 | Raw statements can be ran by using the `statement`-method.
57 |
58 | ```
59 | $db->statement("SELECT * FROM `users` WHERE `username` = 'mark'");
60 | ```
61 |
62 | It also takes an optional second argument with parameters to bind. Let's do the same query but by using bindings instead.
63 |
64 | ```
65 | $db->statement("SELECT * FROM `users` WHERE `username` = ?", ['mark'])
66 | ```
67 |
68 | #### Selecting all rows
69 |
70 | Select all rows from a table using the query builder is quite easy.
71 |
72 | ```
73 | $users = $db->table('users')->get();
74 |
75 | foreach ($users as $user) {
76 | echo "Hello ".$user->username;
77 | }
78 | ```
79 |
80 | #### Select single row
81 |
82 | Often you might want to get just a single database row object, like the current logged in user.
83 |
84 | This can be done quite easy as well.
85 |
86 | ```
87 | $user = $db->table('users')->first();
88 |
89 | echo "Hello ".$user->username;
90 | ```
91 |
92 | > Note that in case of no results. `null` will be returned. To get an exception instead use the `firstOrFail`-method.
93 |
94 | #### Select specific columns
95 |
96 | Want to select only specific columns, like `username`, `name` and `age`.
97 |
98 | ```
99 | $db->table('users')->select(['username', 'name', 'age'])->get();
100 | ```
101 |
102 | You can also use aliases for the selected columns, like you want to get `name` as `fullname`.
103 |
104 | ```
105 | $db->table('users')->select(['username', ['name' => 'fullname'], 'age'])->get();
106 | ```
107 |
108 | #### Where clauses
109 |
110 | You can use where clauses to the query builder to filter your results.
111 |
112 | ##### Basic where clauses
113 |
114 | By default the operator is `=` for where clauses.
115 |
116 | ```
117 | $db->table('users')->where('username', 'mark')->first();
118 | $db->table('users')->where('username', '=', 'mark')->first();
119 | ```
120 |
121 | The two methods above will do exactly the same, however you can use a set of other operators.
122 |
123 | ```
124 | $db->table('users')->where('username', '!=', 'mark')->first();
125 | $db->table('users')->where('age', '>', 18)->first();
126 | $db->table('users')->where('age', '<', 18)->first();
127 | $db->table('users')->where('age', '>=', 18)->first();
128 | $db->table('users')->where('age', '<=', 18)->first();
129 | $db->table('users')->where('age', 'LIKE', 'ma%')->first();
130 | ```
131 |
132 | The default behaviour of the where clauses are all using `and` for combining.
133 |
134 | However you might want to use `or` for some situations.
135 |
136 | ```
137 | $db->table('users')
138 | ->where('username', 'mark')
139 | ->orWhere('username', 'topper')
140 | ->first();
141 | ```
142 |
143 | You mind want to group the where clauses in sub clauses.
144 |
145 | ```
146 | $db->table('users')
147 | ->where('username', 'mark')
148 | ->orWhere(function ($query) {
149 | $query->where('username', 'topper')
150 | ->orWhere('name', 'Mark Topper')
151 | })
152 | ->first();
153 | ```
154 |
155 | ##### Where null
156 |
157 | Want to use the where clause to filter value from that are not null.
158 |
159 | ```
160 | $db->table('users')->whereNull('age')->get();
161 | ```
162 |
163 | ##### Where not null
164 |
165 | Want to use the where clause to filter value from that are null.
166 |
167 | ```
168 | $db->table('users')->whereNotNull('age')->get();
169 | ```
170 |
171 | #### Joining
172 |
173 | You can join additional tables using our joining methods.
174 |
175 | ##### Inner join table
176 |
177 | ```
178 | $db->table('users')
179 | ->join('devices', 'users.id', '=', 'devices.user_id')
180 | ->get();
181 | ```
182 |
183 | > By default the operator is `=` for join clauses.
184 | > So you can actually use `join('devices', 'users.id', 'devices.user_id')`
185 |
186 | ##### Outer join table
187 |
188 | ```
189 | $db->table('users')
190 | ->outerJoin('devices', 'users.id', '=', 'devices.user_id')
191 | ->get();
192 | ```
193 |
194 | ##### Left join table
195 |
196 | ```
197 | $db->table('users')
198 | ->leftJoin('devices', 'users.id', '=', 'devices.user_id')
199 | ->get();
200 | ```
201 | ##### Right join table
202 |
203 | ```
204 | $db->table('users')
205 | ->rightJoin('devices', 'users.id', '=', 'devices.user_id')
206 | ->get();
207 | ```
208 |
209 | ##### Cross join table
210 |
211 | ```
212 | $db->table('users')
213 | ->crossJoin('devices', 'users.id', '=', 'devices.user_id')
214 | ->get();
215 | ```
216 |
217 | ##### Advanced join clause
218 |
219 | ```
220 | $db->table('users')
221 | ->join('devices', function ($join) {
222 | $join->on('users.id', '=', 'devices.user_id')
223 | ->where('devices.platform', 'ios');
224 | })
225 | ->get();
226 | ```
227 |
228 | #### Order results
229 |
230 | You can other by a column, while the second argument controls the direction of the sort and may be either `asc` or `desc`.
231 |
232 | ```
233 | $db->table('users')
234 | ->orderBy('name', 'desc')
235 | ->get();
236 | ```
237 |
238 | You can other by multiple columns.
239 |
240 | ```
241 | $db->table('users')
242 | ->orderBy('fistname', 'desc')
243 | ->orderBy('lastname', 'desc')
244 | ->get();
245 | ```
246 |
247 | ##### Order by random
248 |
249 | Randomize the order
250 |
251 | ```
252 | $db->table('users')
253 | ->orderByRandom()
254 | ->first();
255 | ```
256 |
257 | #### Grouping results
258 |
259 | You can group the results.
260 |
261 | ```
262 | $db->table('users')
263 | ->groupBy('country')
264 | ->get();
265 | ```
266 |
267 | #### Limit results (& offset)
268 |
269 | Limiting results with an offset are often used, specially when paginating.
270 |
271 | ```
272 | $db->table('users')
273 | ->limit(100)
274 | ->offset(100)
275 | ->get();
276 | ```
277 |
278 | #### Count results
279 |
280 | Count rows easily
281 |
282 | ```
283 | $users = $db->table('users')->count();
284 | ```
285 |
286 | #### Raw expressions
287 |
288 | Sometimes you may need to use a raw expression in a query.
289 |
290 | ```
291 | $db->table('users')
292 | ->select([
293 | $db->raw('count(*) as user_count'),
294 | 'status',
295 | ])
296 | ->groupBy('status')
297 | ->get();
298 | ```
299 |
300 | #### Conditional clauses
301 |
302 | Sometimes you might want to only run a certain part of your query when something is true.
303 | You may for instance implement and `where` statement that only applies if a user is logged in.
304 |
305 | ```
306 | $currentUserId = 1;
307 | $loggedIn = true;
308 |
309 | $db->table('users')
310 | ->when($loggedIn, function ($query) {
311 | $query->where('id', '=', $currentUserId);
312 | })
313 | ->get();
314 | ```
315 |
316 | #### Insert rows
317 |
318 | The `insert` method accepts an array of column names and values.
319 |
320 | ```
321 | $db->table('users')->insert([
322 | ['email' => 'mark@example.com', 'username' => 'mark'],
323 | ['email' => 'john@example.com', 'username' => 'john'],
324 | ]);
325 | ```
326 |
327 | Or you can insert a single row.
328 |
329 | ```
330 | $db->table('users')->insert(
331 | ['email' => 'mark@example.com', 'username' => 'mark']
332 | );
333 | ```
334 |
335 | ##### Auto incrementing IDs
336 |
337 | Want to insert a row and get the auto incremented ID? You can do this using the `insertGetId` method.
338 |
339 | ```
340 | $id = $db->table('users')->insertGetId(
341 | ['email' => 'mark@example.com', 'username' => 'mark']
342 | );
343 | ```
344 |
345 | #### Update rows
346 |
347 | Update `name` for the for the user with the `username` set to `mark`?
348 |
349 | ```
350 | $db->table('user')->where('username', 'mark')->update(['name' => 'Foobar']);
351 | ```
352 |
353 | #### Deleting rows
354 |
355 | Deleting rows have never been easier.
356 |
357 | ```
358 | $db->table('users')->where('last_login', '<', '2016-01-01 00:00:00')->delete();
359 | ```
360 |
361 | If you wish to truncate the entire table, which will remove all rows and reset the auto-incrementing ID to zero, you may use the `truncate` method.
362 |
363 | ```
364 | $db->table('users')->truncate();
365 | ```
366 |
367 | #### Connection swapping
368 |
369 | Have multiple connections configured you may swap between connections. The default connection is used unless anything else specified.
370 |
371 | ```
372 | $db->connection('connection-name')
373 | ->table('users')
374 | ->get();
375 | ```
376 |
--------------------------------------------------------------------------------
/src/Fist/Database/src/Connectors/Connection.php:
--------------------------------------------------------------------------------
1 | configure($repository);
25 | }
26 | }
27 |
28 | public function getPdo()
29 | {
30 | return $this->pdo;
31 | }
32 |
33 | public function configure(RepositoryInterface $repository)
34 | {
35 | if ($this->repository != $repository) {
36 | $this->repository = $repository;
37 |
38 | $this->pdo = $this->newPdo($repository);
39 | }
40 | }
41 |
42 | public function table($name)
43 | {
44 | return $this->callQueryBuilder('table', [$name]);
45 | }
46 |
47 | protected function callQueryBuilder($method, array $parameters = [])
48 | {
49 | return call_user_func_array([$this->newQueryBuilder(), $method], $parameters);
50 | }
51 |
52 | protected function newQueryBuilder()
53 | {
54 | return new QueryBuilder($this, $this->newQueryGrammar());
55 | }
56 |
57 | public function statement($sql, array $parameters = [])
58 | {
59 | return $this->createStatement(
60 | $this->pdo->prepare($sql),
61 | $parameters
62 | )->execute();
63 | }
64 |
65 | public function raw($sql, array $parameters = [])
66 | {
67 | return $this->createStatement(
68 | $this->pdo->prepare($sql),
69 | $parameters
70 | );
71 | }
72 |
73 | public function getLastInsertedId()
74 | {
75 | return $this->pdo->lastInsertId();
76 | }
77 |
78 | protected function createStatement(PDOStatement $statement, array $parameters = [])
79 | {
80 | return new Statement($this->pdo, $statement, $parameters);
81 | }
82 |
83 | public function getTablePrefix()
84 | {
85 | return $this->repository->get('prefix');
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Fist/Database/src/Connectors/ConnectionInterface.php:
--------------------------------------------------------------------------------
1 | get('hostname', 'localhost');
14 | $database = $repository->get('database', '');
15 |
16 | $pdo = new PDO(
17 | "mysql:host={$hostname};dbname={$database}",
18 | $repository->get('username'),
19 | $repository->get('password')
20 | );
21 |
22 | $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
23 |
24 | return $pdo;
25 | }
26 |
27 | public function newQueryGrammar()
28 | {
29 | return new MysqlGrammar();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Fist/Database/src/Connectors/SqliteConnection.php:
--------------------------------------------------------------------------------
1 | get('database');
14 |
15 | $pdo = new PDO("sqlite:{$database}");
16 |
17 | $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
18 |
19 | return $pdo;
20 | }
21 |
22 | public function newQueryGrammar()
23 | {
24 | return new SqliteGrammar();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Fist/Database/src/Database.php:
--------------------------------------------------------------------------------
1 | repository = $repository;
19 | }
20 |
21 | public function getRepository()
22 | {
23 | return $this->repository;
24 | }
25 |
26 | public function setRepository(RepositoryInterface $repository)
27 | {
28 | $this->repository = $repository;
29 | }
30 |
31 | public function getDefaultConnection($default = 'default')
32 | {
33 | return $this->repository->get('default.connection', $default);
34 | }
35 |
36 | public function getDefaultDriver($default = 'mysql')
37 | {
38 | return $this->repository->get('default.driver', $default);
39 | }
40 |
41 | /**
42 | * Get connection configuration repository by name.
43 | *
44 | * @param string $key
45 | * @param null|string $default
46 | *
47 | * @return null|\Fist\Repository\RepositoryInterface
48 | */
49 | public function getConnection($key, $default = null)
50 | {
51 | $connection = $this->repository->get('connections.'.$key, $default);
52 |
53 | if ($connection instanceof Closure) {
54 | $connection = $connection();
55 | }
56 |
57 | if (is_array($connection)) {
58 | $connection = new ArrayRepository($connection);
59 | }
60 |
61 | return $connection;
62 | }
63 |
64 | public function hasConnection($key)
65 | {
66 | return $this->repository->has('connections.'.$key);
67 | }
68 |
69 | public function setConnection($key, $value)
70 | {
71 | if (is_string($value)) {
72 | $value = new $value();
73 | }
74 |
75 | $this->repository->set('connections.'.$key, $value);
76 | }
77 |
78 | /**
79 | * Get driver by name.
80 | *
81 | * @param string $key
82 | * @param null|string $default
83 | *
84 | * @return null|\Fist\Database\Connectors\Connection
85 | */
86 | public function getDriver($key, $default = null)
87 | {
88 | $driver = $this->repository->get('drivers.'.$key, $default);
89 |
90 | if ($driver instanceof Closure) {
91 | $driver = $driver();
92 | }
93 |
94 | if (is_string($driver)) {
95 | $driver = new $driver();
96 | }
97 |
98 | return clone $driver;
99 | }
100 |
101 | public function hasDriver($key)
102 | {
103 | return $this->repository->has('drivers.'.$key);
104 | }
105 |
106 | public function setDriver($key, $value)
107 | {
108 | if (is_string($value)) {
109 | $value = new $value();
110 | }
111 |
112 | $this->repository->set('drivers.'.$key, $value);
113 | }
114 |
115 | public function setDrivers(array $drivers)
116 | {
117 | foreach ($drivers as $name => $driver) {
118 | $this->setDriver($name, $driver);
119 | }
120 | }
121 |
122 | public function setDefaultConnection($connection)
123 | {
124 | $this->setDefaultValue('connection', $connection);
125 | }
126 |
127 | public function setDefaultDriver($driver)
128 | {
129 | $this->setDefaultValue('driver', $driver);
130 | }
131 |
132 | public function setDefaultValue($key, $value)
133 | {
134 | $this->repository->set('default.'.$key, $value);
135 | }
136 |
137 | public function setDefaultValues(array $values)
138 | {
139 | foreach ($values as $name => $value) {
140 | $this->setDefaultValue($name, $value);
141 | }
142 | }
143 |
144 | public function setConnections(array $connections)
145 | {
146 | foreach ($connections as $name => $connection) {
147 | if (is_array($connection)) {
148 | $connection = new ArrayRepository($connection);
149 | }
150 |
151 | $this->setConnection($name, $connection);
152 | }
153 | }
154 |
155 | public function connection($name = null)
156 | {
157 | if (is_null($name)) {
158 | $name = $this->getDefaultConnection();
159 | }
160 |
161 | $connection = $this->getConnection($name);
162 |
163 | if (is_null($connection)) {
164 | throw new DatabaseException("Connection [{$name}] does not exists.");
165 | }
166 |
167 | $driverName = $connection->get('driver', $this->getDefaultDriver());
168 |
169 | if ($this->hasConnectedDriver($driverName, $name)) {
170 | $driver = $this->getConnectedDriver($driverName, $name);
171 | } else {
172 | $driver = $this->getDriver($driverName);
173 | }
174 |
175 | if (is_null($driver)) {
176 | throw new DatabaseException("Driver [{$driverName}] does not exists.");
177 | }
178 |
179 | $this->setConnectedDriver($driverName, $name, $driver);
180 |
181 | $driver->configure($connection);
182 |
183 | return $driver;
184 | }
185 |
186 | protected function getConnectedDriver($driverName, $connectionName, $default = null)
187 | {
188 | if ($this->hasConnectedDriver($driverName, $connectionName)) {
189 | return $this->connectedDrivers[$driverName][$connectionName];
190 | }
191 |
192 | return $default;
193 | }
194 |
195 | protected function hasConnectedDriver($driverName, $connectionName)
196 | {
197 | if (isset($this->connectedDrivers[$driverName])) {
198 | return isset($this->connectedDrivers[$driverName][$connectionName]);
199 | }
200 |
201 | return false;
202 | }
203 |
204 | protected function setConnectedDriver($driverName, $connectionName, ConnectionInterface $connection)
205 | {
206 | if (! isset($this->connectedDrivers[$driverName])) {
207 | $this->connectedDrivers[$driverName] = [];
208 | }
209 |
210 | $this->connectedDrivers[$driverName][$connectionName] = $connection;
211 | }
212 |
213 | public function table($name)
214 | {
215 | return $this->callConnectionDriver('table', [$name]);
216 | }
217 |
218 | public function createTable($name, $closure)
219 | {
220 | return $this->callConnectionDriver('createTable', [$name, $closure]);
221 | }
222 |
223 | protected function callConnectionDriver($method, array $arguments = [])
224 | {
225 | return call_user_func_array([$this->connection(), $method], $arguments);
226 | }
227 |
228 | public function statement($sql, array $parameters = [])
229 | {
230 | return $this->callConnectionDriver('statement', [$sql, $parameters]);
231 | }
232 |
233 | public function raw($sql, array $parameters = [])
234 | {
235 | return $this->callConnectionDriver('raw', [$sql, $parameters]);
236 | }
237 |
238 | public function getLastInsertedId()
239 | {
240 | return $this->callConnectionDriver('getLastInsertedId');
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/src/Fist/Database/src/DatabaseException.php:
--------------------------------------------------------------------------------
1 | connection = $connection;
39 |
40 | $this->grammar = $grammar;
41 | }
42 |
43 | public function getPrimaryKey()
44 | {
45 | return $this->primaryKey;
46 | }
47 |
48 | public function table($name)
49 | {
50 | $this->table = $name;
51 |
52 | return $this;
53 | }
54 |
55 | public function select(array $columns)
56 | {
57 | $this->select = $columns;
58 |
59 | return $this;
60 | }
61 |
62 | protected function makeWhere($aggregator, $column, $operator, $value = null)
63 | {
64 | if (is_null($value)) {
65 | $value = $operator;
66 |
67 | $operator = '=';
68 | }
69 |
70 | $this->where[] = [
71 | 'column' => $column,
72 | 'operator' => $operator,
73 | 'value' => $value,
74 | 'aggregator' => $aggregator,
75 | ];
76 |
77 | return $this;
78 | }
79 |
80 | public function where($column, $operator, $value = null)
81 | {
82 | return $this->makeWhere('AND', $column, $operator, $value);
83 | }
84 |
85 | public function orWhere($column, $operator, $value = null)
86 | {
87 | return $this->makeWhere('OR', $column, $operator, $value);
88 | }
89 |
90 | public function orderBy($column, $direction = 'ASC')
91 | {
92 | $this->orders[] = [
93 | 'column' => $column,
94 | 'direction' => $direction,
95 | 'random' => false,
96 | ];
97 |
98 | return $this;
99 | }
100 |
101 | public function orderByRandom()
102 | {
103 | $this->orders[] = [
104 | 'random' => true,
105 | ];
106 |
107 | return $this;
108 | }
109 |
110 | public function groupBy($column)
111 | {
112 | $this->groups[] = $column;
113 |
114 | return $this;
115 | }
116 |
117 | protected function makeJoin($type, $table, $key, $operator, $foreign)
118 | {
119 | if (is_null($foreign)) {
120 | $foreign = $operator;
121 |
122 | $operator = '=';
123 | }
124 |
125 | $this->joins[] = [
126 | 'type' => $type,
127 | 'table' => $table,
128 | 'key' => $key,
129 | 'foreign' => $foreign,
130 | 'operator' => $operator,
131 | ];
132 |
133 | return $this;
134 | }
135 |
136 | public function innerJoin($table, $key, $operator, $foreign = null)
137 | {
138 | return $this->makeJoin('INNER', $table, $key, $operator, $foreign);
139 | }
140 |
141 | public function rightJoin($table, $key, $operator, $foreign = null)
142 | {
143 | return $this->makeJoin('RIGHT', $table, $key, $operator, $foreign);
144 | }
145 |
146 | public function leftJoin($table, $key, $operator, $foreign = null)
147 | {
148 | return $this->makeJoin('LEFT', $table, $key, $operator, $foreign);
149 | }
150 |
151 | public function outerJoin($table, $key, $operator, $foreign = null)
152 | {
153 | return $this->makeJoin('OUTER', $table, $key, $operator, $foreign);
154 | }
155 |
156 | public function truncate()
157 | {
158 | $this->aggregator = 'truncate';
159 |
160 | return $this->connection->statement(
161 | $this->toSql()
162 | );
163 | }
164 |
165 | public function get()
166 | {
167 | $this->aggregator = 'select';
168 |
169 | return $this->connection->statement(
170 | $this->toSql()
171 | );
172 | }
173 |
174 | public function update(array $values)
175 | {
176 | $this->aggregator = 'update';
177 |
178 | $this->values = $values;
179 |
180 | return $this->connection->statement(
181 | $this->toSql()
182 | );
183 | }
184 |
185 | public function insert(array $values)
186 | {
187 | $this->aggregator = 'insert';
188 |
189 | $this->values = $values;
190 |
191 | return $this->connection->statement(
192 | $this->toSql()
193 | );
194 | }
195 |
196 | public function delete()
197 | {
198 | $this->aggregator = 'delete';
199 |
200 | return $this->connection->statement(
201 | $this->toSql()
202 | );
203 | }
204 |
205 | public function first()
206 | {
207 | $this->aggregator = 'select';
208 |
209 | $this->limit = 1;
210 |
211 | $results = $this->connection->statement(
212 | $this->toSql()
213 | );
214 |
215 | return isset($results[0]) ? $results[0] : null;
216 | }
217 |
218 | public function last()
219 | {
220 | $this->aggregator = 'select';
221 |
222 | $this->orderBy(
223 | $this->getPrimaryKey(),
224 | 'DESC'
225 | );
226 | $this->limit = 1;
227 |
228 | $results = $this->connection->statement(
229 | $this->toSql()
230 | );
231 |
232 | return isset($results[0]) ? $results[0] : null;
233 | }
234 |
235 | public function toSql()
236 | {
237 | return $this->grammar->toSql($this);
238 | }
239 |
240 | public function getAggregator()
241 | {
242 | return $this->aggregator;
243 | }
244 |
245 | public function getTable()
246 | {
247 | return $this->table;
248 | }
249 |
250 | public function getTablePrefix()
251 | {
252 | return $this->connection->getTablePrefix();
253 | }
254 |
255 | public function getSelect()
256 | {
257 | return $this->select;
258 | }
259 |
260 | public function getWhereStatements()
261 | {
262 | return $this->where;
263 | }
264 |
265 | public function getOrders()
266 | {
267 | return $this->orders;
268 | }
269 |
270 | public function getLimit()
271 | {
272 | return $this->limit;
273 | }
274 |
275 | public function getOffset()
276 | {
277 | return $this->offset;
278 | }
279 |
280 | public function getValues()
281 | {
282 | return $this->values;
283 | }
284 |
285 | public function getGroups()
286 | {
287 | return $this->groups;
288 | }
289 |
290 | public function getJoins()
291 | {
292 | return $this->joins;
293 | }
294 | }
295 |
--------------------------------------------------------------------------------
/src/Fist/Database/src/Query/Grammars/Grammar.php:
--------------------------------------------------------------------------------
1 | tablePrefix = $builder->getTablePrefix();
17 |
18 | return $this->compileAggregator(
19 | $builder,
20 | $builder->getAggregator()
21 | );
22 | }
23 |
24 | public function compileAggregator(Builder $builder, $aggregator)
25 | {
26 | if (is_null($aggregator)) {
27 | throw new DatabaseException('Missing aggregator.');
28 | }
29 |
30 | if (! in_array($aggregator, $this->aggregators)) {
31 | throw new DatabaseException("Invalid aggregator [{$aggregator}].");
32 | }
33 |
34 | $method = 'compile'.ucfirst($aggregator).'Aggregator';
35 |
36 | if (method_exists($this, $method)) {
37 | return $this->$method($builder, $aggregator);
38 | }
39 |
40 | $method = 'get'.ucfirst($aggregator).'AggregatorPrefix';
41 |
42 | $prefix = method_exists($this, $method) ? $this->$method($builder, $aggregator) : null;
43 |
44 | $method = 'get'.ucfirst($aggregator).'AggregatorSuffix';
45 |
46 | $suffix = method_exists($this, $method) ? $this->$method($builder, $aggregator) : null;
47 |
48 | $string = '';
49 |
50 | foreach ($this->getAggregatorComponents($aggregator) as $component) {
51 | $method = 'compile'.ucfirst($component).'ComponentFor'.ucfirst($aggregator).'Aggregator';
52 |
53 | if (! method_exists($this, $method)) {
54 | $method = 'compile'.ucfirst($component).'Component';
55 | }
56 |
57 | if ($content = $this->$method($builder, $aggregator)) {
58 | if (! is_array($content)) {
59 | $content = [$content, true];
60 | }
61 |
62 | $space = isset($content[1]) ? $content[1] : true;
63 | $string .= $space ? ' ' : '';
64 | $string .= $content[0];
65 | }
66 | }
67 |
68 | return "{$prefix}{$string}{$suffix}";
69 | }
70 |
71 | public function getAggregatorComponents($aggregator)
72 | {
73 | $method = 'get'.ucfirst($aggregator).'Components';
74 |
75 | if (method_exists($this, $method)) {
76 | return $this->$method();
77 | }
78 |
79 | return $this->getComponents();
80 | }
81 |
82 | /**
83 | * Convert an array of column names into a delimited string.
84 | *
85 | * @param array $columns
86 | *
87 | * @return string
88 | */
89 | public function columnize(array $columns)
90 | {
91 | if (empty($columns)) {
92 | return '*';
93 | }
94 |
95 | return implode(', ', array_map([$this, 'wrap'], $columns));
96 | }
97 |
98 | /**
99 | * Wrap a value in keyword identifiers.
100 | *
101 | * @param string $value
102 | * @param bool $prefixAlias
103 | *
104 | * @return string
105 | */
106 | public function wrap($value, $prefixAlias = false)
107 | {
108 | // Work for "as" aliases.
109 | if (is_array($value) && count($value) === 2) {
110 | if ($prefixAlias) {
111 | $value[1] = $this->tablePrefix.$value[1];
112 | }
113 |
114 | return $this->wrap($value[0]).' as '.$this->wrap($value[1]);
115 | }
116 |
117 | $wrapped = [];
118 |
119 | $segments = explode('.', $value);
120 |
121 | // Wrap first segment as table, and the rest (if any) as regular values
122 | foreach ($segments as $key => $segment) {
123 | if ($key == 0 && count($segments) > 1) {
124 | $wrapped[] = $this->wrapTable($segment);
125 | } else {
126 | $wrapped[] = $this->wrapColumn($segment);
127 | }
128 | }
129 |
130 | return implode('.', $wrapped);
131 | }
132 |
133 | public function wrapTable($table)
134 | {
135 | $prefix = $this->tablePrefix;
136 |
137 | return "`{$prefix}{$table}`";
138 | }
139 |
140 | public function wrapColumn($column)
141 | {
142 | return "`{$column}`";
143 | }
144 |
145 | /**
146 | * Wrap a single string in keyword identifiers.
147 | *
148 | * @param string $value
149 | *
150 | * @return string
151 | */
152 | public function wrapValue($value)
153 | {
154 | if ($value === '*') {
155 | return $value;
156 | }
157 |
158 | return '"'.str_replace('"', '""', $value).'"';
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/Fist/Database/src/Query/Grammars/GrammarInterface.php:
--------------------------------------------------------------------------------
1 | columnize(
38 | $builder->getSelect()
39 | );
40 | }
41 |
42 | protected function compileValuesComponent(Builder $builder, $aggregator)
43 | {
44 | if (in_array($aggregator, ['select', 'insert', 'delete', 'truncate'])) {
45 | return;
46 | }
47 |
48 | $values = $builder->getValues();
49 |
50 | return 'SET '.implode(', ', array_map(function ($column, $value) {
51 | $column = $this->wrapColumn($column);
52 | $value = $this->wrapValue($value);
53 |
54 | return "{$column} = {$value}";
55 | }, array_keys($values), $values));
56 | }
57 |
58 | protected function compileValuesComponentForInsertAggregator(Builder $builder)
59 | {
60 | $values = $builder->getValues();
61 | $keys = isset($values[0]) ? array_keys($values[0]) : [];
62 |
63 | return '('.implode(', ', array_map(function ($column) {
64 | return $this->wrapColumn($column);
65 | }, $keys)).') VALUES '.implode(', ', array_map(function ($items) {
66 | return '('.implode(', ', array_map(function ($item) {
67 | return $this->wrapValue($item);
68 | }, $items)).')';
69 | }, $builder->getValues()));
70 | }
71 |
72 | protected function compileTableComponent(Builder $builder, $aggregator)
73 | {
74 | $table = $this->wrapTable(
75 | $builder->getTable()
76 | );
77 |
78 | switch ($aggregator) {
79 | case 'select': return "FROM {$table}";
80 | default: return $table;
81 | }
82 | }
83 |
84 | protected function compileWheresComponent(Builder $builder)
85 | {
86 | $wheres = [];
87 |
88 | foreach ($builder->getWhereStatements() as $index => $where) {
89 | $column = $this->wrapColumn($where['column']);
90 | $operator = $where['operator'];
91 | $value = $this->wrapValue($where['value']);
92 | $aggregator = $index > 0 ? $where['aggregator'] : 'WHERE';
93 |
94 | $wheres[] = "{$aggregator} {$column} {$operator} {$value}";
95 | }
96 |
97 | return implode(' ', $wheres);
98 | }
99 |
100 | protected function compileOrdersComponent(Builder $builder)
101 | {
102 | $orders = $builder->getOrders();
103 |
104 | if (empty($orders)) {
105 | return;
106 | }
107 |
108 | return 'ORDER BY '.implode(', ', array_map(function ($order) {
109 | if ($order['random']) {
110 | return 'RAND()';
111 | }
112 |
113 | $column = $this->wrapColumn($order['column']);
114 |
115 | $direction = isset($order['direction']) ? $order['direction'] : null;
116 |
117 | if (is_null($direction)) {
118 | return $column;
119 | }
120 |
121 | return "{$column} {$direction}";
122 | }, $orders));
123 | }
124 |
125 | protected function compileLimitComponent(Builder $builder)
126 | {
127 | $limit = $builder->getLimit();
128 |
129 | if (is_null($limit)) {
130 | return;
131 | }
132 |
133 | return "LIMIT {$limit}";
134 | }
135 |
136 | protected function compileOffsetComponent(Builder $builder)
137 | {
138 | $offset = $builder->getOffset();
139 |
140 | if (is_null($offset)) {
141 | return;
142 | }
143 |
144 | return [", {$offset}", false];
145 | }
146 |
147 | protected function compileGroupsComponent(Builder $builder)
148 | {
149 | $groups = $builder->getGroups();
150 |
151 | if (empty($groups)) {
152 | return;
153 | }
154 |
155 | return 'GROUP BY '.implode(', ', array_map(function ($group) {
156 | return $this->wrapColumn($group);
157 | }, $groups));
158 | }
159 |
160 | protected function compileJoinsComponent(Builder $builder)
161 | {
162 | $joins = $builder->getJoins();
163 |
164 | if (empty($joins)) {
165 | return;
166 | }
167 |
168 | return implode(' ', array_map(function ($join) {
169 | $type = $join['type'];
170 | $table = $this->wrapTable($join['table']);
171 | $key = $this->wrapColumn($join['key']);
172 | $operator = $join['operator'];
173 | $foreign = $this->wrapColumn($join['foreign']);
174 |
175 | return "{$type} JOIN {$table} ON {$key} {$operator} {$foreign}";
176 | }, $joins));
177 | }
178 |
179 | public function getComponents()
180 | {
181 | return $this->components;
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/Fist/Database/src/Query/Grammars/SqliteGrammar.php:
--------------------------------------------------------------------------------
1 | wrapTable(
12 | $builder->getTable()
13 | );
14 |
15 | return "DELETE FROM {$table}";
16 | }
17 |
18 | protected function compileOrdersComponent(Builder $builder)
19 | {
20 | $orders = $builder->getOrders();
21 |
22 | if (empty($orders)) {
23 | return;
24 | }
25 |
26 | return 'ORDER BY '.implode(', ', array_map(function ($order) {
27 | if ($order['random']) {
28 | return 'RANDOM()';
29 | }
30 |
31 | $column = $this->wrapColumn($order['column']);
32 |
33 | $direction = isset($order['direction']) ? $order['direction'] : null;
34 |
35 | if (is_null($direction)) {
36 | return $column;
37 | }
38 |
39 | return "{$column} {$direction}";
40 | }, $orders));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Fist/Database/src/Query/Statement.php:
--------------------------------------------------------------------------------
1 | pdo = $pdo;
41 |
42 | $this->statement = $statement;
43 |
44 | $this->bindings = $bindings;
45 | }
46 |
47 | /**
48 | * Execute query statement.
49 | *
50 | * @return array
51 | */
52 | public function execute()
53 | {
54 | if ($this->statement->execute($this->bindings)) {
55 | return $this->statement->fetchAll(PDO::FETCH_OBJ);
56 | }
57 | }
58 |
59 | /**
60 | * Get query string.
61 | *
62 | * @return string
63 | */
64 | public function toSqlWithoutBindings()
65 | {
66 | return $this->statement->queryString;
67 | }
68 |
69 | /**
70 | * Get query string with bindings.
71 | *
72 | * @return string
73 | */
74 | public function toSql()
75 | {
76 | return array_reduce(
77 | $this->bindings,
78 | function ($sql, $binding) {
79 | return preg_replace('/\?/', $this->quote($binding), $sql, 1);
80 | },
81 | $this->statement->queryString
82 | );
83 | }
84 |
85 | /**
86 | * Get PDOStatement instance.
87 | *
88 | * @return \PDOStatement
89 | */
90 | public function getPdoStatement()
91 | {
92 | return $this->statement;
93 | }
94 |
95 | /**
96 | * Get binding parameters.
97 | *
98 | * @return array
99 | */
100 | public function getBindings()
101 | {
102 | return $this->bindings;
103 | }
104 |
105 | /**
106 | * Quote parameter according to its type.
107 | *
108 | * @param $parameter
109 | *
110 | * @return string
111 | */
112 | protected function quote($parameter)
113 | {
114 | if (is_string($parameter)) {
115 | return $this->pdo->quote($parameter);
116 | }
117 |
118 | return $parameter;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/Fist/Database/tests/DatabaseConnectionTest.php:
--------------------------------------------------------------------------------
1 | [
22 | 'driver' => 'mysql',
23 | 'database' => 'database',
24 | 'hostname' => 'localhost',
25 | 'username' => '',
26 | 'password' => '',
27 | ],
28 | 'sqlite' => [
29 | 'driver' => 'sqlite',
30 | 'database' => ':memory:', // in-memory
31 | 'hostname' => 'localhost',
32 | 'username' => '',
33 | 'password' => '',
34 | ],
35 | 'tmp' => [
36 | 'driver' => 'sqlite',
37 | 'database' => '', // temporary
38 | 'hostname' => 'localhost',
39 | 'username' => '',
40 | 'password' => '',
41 | ],
42 | ];
43 |
44 | public function testConnectionBuildingUsingArrayRepository()
45 | {
46 | $db = new Database(
47 | new ArrayRepository()
48 | );
49 |
50 | $this->assertInstanceOf(RepositoryInterface::class, $db->getRepository());
51 | $this->assertInstanceOf(ArrayRepository::class, $db->getRepository());
52 | }
53 |
54 | public function testConnectionBuildingUsingContainerRepository()
55 | {
56 | $db = new Database(
57 | new ContainerRepository(
58 | new Container()
59 | )
60 | );
61 |
62 | $this->assertInstanceOf(RepositoryInterface::class, $db->getRepository());
63 | $this->assertInstanceOf(ContainerRepository::class, $db->getRepository());
64 | }
65 |
66 | public function testDefaultConnection()
67 | {
68 | $db = new Database(
69 | new ArrayRepository()
70 | );
71 |
72 | $this->setDatabaseConnectionsAndDrivers($db);
73 |
74 | $this->assertEquals('default', $db->getDefaultConnection());
75 | $this->throwsException(function () use ($db) {
76 | $this->assertInstanceOf(MysqlConnection::class, $db->connection());
77 | }, DatabaseException::class, 'Connection [default] does not exists.');
78 |
79 | $db->setDefaultConnection('mysql');
80 |
81 | $this->assertEquals('mysql', $db->getDefaultConnection());
82 | $this->throwsException(function () use ($db) {
83 | $this->assertInstanceOf(MysqlConnection::class, $db->connection());
84 | }, PDOException::class, [
85 | "SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO)",
86 | "SQLSTATE[HY000] [1044] Access denied for user ''@'localhost' to database 'database'",
87 | ]);
88 |
89 | $db->setDefaultConnection('sqlite');
90 |
91 | $this->assertEquals('sqlite', $db->getDefaultConnection());
92 | $this->assertInstanceOf(SqliteConnection::class, $db->connection());
93 | }
94 |
95 | public function testDefaultDriver()
96 | {
97 | $db = new Database(
98 | new ArrayRepository()
99 | );
100 |
101 | $this->setDatabaseConnectionsAndDrivers($db);
102 | $db->setConnection('default', []);
103 |
104 | $this->assertEquals('mysql', $db->getDefaultDriver());
105 | $this->throwsException(function () use ($db) {
106 | $db->connection()->statement('SELECT * FROM test');
107 | }, PDOException::class, [
108 | 'SQLSTATE[3D000]: Invalid catalog name: 1046 No database selected',
109 | "SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO)",
110 | ]);
111 |
112 | $db->setDefaultDriver('sqlite');
113 | $this->assertEquals('sqlite', $db->getDefaultDriver());
114 | $this->assertInstanceOf(SqliteConnection::class, $db->connection());
115 | }
116 |
117 | public function testConnectionSwapping()
118 | {
119 | $db = new Database(
120 | new ArrayRepository()
121 | );
122 |
123 | $this->setDatabaseConnectionsAndDrivers($db);
124 | $db->setDefaultConnection('mysql');
125 |
126 | $this->throwsException(function () use ($db) {
127 | $this->assertInstanceOf(MysqlConnection::class, $db->connection());
128 | }, PDOException::class, [
129 | "SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO)",
130 | "SQLSTATE[HY000] [1044] Access denied for user ''@'localhost' to database 'database'",
131 | ]);
132 |
133 | $this->assertInstanceOf(SqliteConnection::class, $db->connection('sqlite'));
134 | }
135 |
136 | public function testDriverConnectionAreShared()
137 | {
138 | $db = new Database(
139 | new ContainerRepository(
140 | new Container(),
141 | 'db.'
142 | )
143 | );
144 |
145 | $this->setDatabaseConnectionsAndDrivers($db);
146 | $db->setDefaultConnection('sqlite');
147 |
148 | $this->runOnDatabaseConnections($db, ['sqlite', 'tmp'], function (ConnectionInterface $connection) {
149 | $connection->statement('CREATE TABLE items (name VARCHAR(50))');
150 | });
151 |
152 | $this->runOnDatabaseConnections($db, [
153 | 'sqlite',
154 | 'tmp',
155 | 'sqlite',
156 | 'tmp',
157 | null,
158 | ], function (ConnectionInterface $connection) {
159 | $this->throwsException(function () use ($connection) {
160 | $connection->statement('CREATE TABLE items (name VARCHAR(50))');
161 | }, PDOException::class, 'SQLSTATE[HY000]: General error: 1 table items already exists');
162 | });
163 | }
164 |
165 | public function testDriverSwapping()
166 | {
167 | $db = new Database(
168 | $repository = new ContainerRepository(
169 | new Container(),
170 | 'db.'
171 | )
172 | );
173 |
174 | $this->setDatabaseConnectionsAndDrivers($db);
175 | $db->setDefaultConnection('sqlite');
176 |
177 | // Test getting default connection
178 | $connection = $db->connection();
179 | $this->assertInstanceOf(SqliteConnection::class, $connection);
180 |
181 | // Change from in-memory to temporary database
182 | $db->setConnection('sqlite', [
183 | 'driver' => 'mysql',
184 | ]);
185 |
186 | // Connection fails using the mysql setup since it's not setup.
187 | // So we expect it to fail
188 | $this->throwsException(function () use ($db) {
189 | $db->connection()->statement('SELECT * FROM test');
190 | }, PDOException::class, [
191 | 'SQLSTATE[3D000]: Invalid catalog name: 1046 No database selected',
192 | "SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO)",
193 | ]);
194 | }
195 |
196 | public function testSharedDriverConnectionsUpdateSettings()
197 | {
198 | $db = new Database(
199 | $repository = new ContainerRepository(
200 | new Container(), 'db.'
201 | )
202 | );
203 |
204 | $this->setDatabaseConnectionsAndDrivers($db);
205 | $db->setDefaultConnection('sqlite');
206 |
207 | $db->statement('CREATE TABLE items (name VARCHAR(50))');
208 |
209 | // Ensure that table exists
210 | $this->assertEquals([], $db->table('items')->get());
211 |
212 | // Change from in-memory to temporary database
213 | $db->setConnection('sqlite', [
214 | 'driver' => 'sqlite',
215 | 'database' => '',
216 | ]);
217 |
218 | // Ensure that table does not exists on the new connection
219 | $this->throwsException(function () use ($db) {
220 | $db->table('items')->get();
221 | }, PDOException::class, [
222 | 'SQLSTATE[HY000]: General error: 1 no such table: items',
223 | ]);
224 |
225 | // The change should make sure that this does not fail,
226 | // even that it is the same driver and same connection,
227 | // but since the connection variable have been changed.
228 | $db->statement('CREATE TABLE items (name VARCHAR(50))');
229 |
230 | // Ensure that table exists
231 | $this->assertEquals([], $db->table('items')->get());
232 | }
233 |
234 | public function testRawQueries()
235 | {
236 | $db = $this->prepareDatabase();
237 |
238 | $statement = $db->raw("SELECT * FROM items WHERE name = 'foo'");
239 |
240 | $this->assertInstanceOf(Statement::class, $statement);
241 | $this->assertEquals("SELECT * FROM items WHERE name = 'foo'", $statement->toSql());
242 |
243 | $results = $statement->execute();
244 |
245 | $this->assertTrue(is_array($results));
246 | $this->assertCount(1, $results);
247 | $this->assertEquals('foo', $results[0]->name);
248 | }
249 |
250 | public function testRawQueriesWithParameters()
251 | {
252 | $db = $this->prepareDatabase();
253 |
254 | $statement = $db->raw('SELECT * FROM items WHERE name = ? OR name = ?', ['foo', 1]);
255 |
256 | $this->assertInstanceOf(Statement::class, $statement);
257 | $this->assertEquals('SELECT * FROM items WHERE name = ? OR name = ?', $statement->toSqlWithoutBindings());
258 | $this->assertEquals("SELECT * FROM items WHERE name = 'foo' OR name = 1", $statement->toSql());
259 |
260 | $results = $statement->execute();
261 |
262 | $this->assertTrue(is_array($results));
263 | $this->assertCount(1, $results);
264 | $this->assertEquals('foo', $results[0]->name);
265 | }
266 |
267 | public function testStatements()
268 | {
269 | $db = $this->prepareDatabase();
270 |
271 | $results = $db->statement("SELECT * FROM items WHERE name = 'foo'");
272 |
273 | $this->assertTrue(is_array($results));
274 | $this->assertCount(1, $results);
275 | $this->assertEquals('foo', $results[0]->name);
276 | }
277 |
278 | public function testStatementsWithParameters()
279 | {
280 | $db = $this->prepareDatabase();
281 |
282 | $results = $db->statement('SELECT * FROM items WHERE name = ?', ['foo']);
283 |
284 | $this->assertTrue(is_array($results));
285 | $this->assertCount(1, $results);
286 | $this->assertEquals('foo', $results[0]->name);
287 | }
288 |
289 | public function testGettingLastInsertedId()
290 | {
291 | $db = $this->prepareDatabase();
292 |
293 | $this->assertEquals(1, $db->getLastInsertedId());
294 | }
295 |
296 | public function testTruncatingTables()
297 | {
298 | $db = $this->prepareDatabase();
299 |
300 | $results = $db->statement('SELECT * FROM items');
301 |
302 | $this->assertCount(1, $results);
303 |
304 | $db->table('items')->truncate();
305 |
306 | $results = $db->statement('SELECT * FROM items');
307 |
308 | $this->assertCount(0, $results);
309 | }
310 |
311 | public function testSelectRows()
312 | {
313 | $db = $this->prepareDatabase();
314 |
315 | $results = $db->table('items')->get();
316 |
317 | $this->assertTrue(is_array($results));
318 | $this->assertCount(1, $results);
319 | $this->assertEquals('foo', $results[0]->name);
320 |
321 | $results = $db->table('items')->where('name', 'foo')->get();
322 |
323 | $this->assertTrue(is_array($results));
324 | $this->assertCount(1, $results);
325 | $this->assertEquals('foo', $results[0]->name);
326 |
327 | $results = $db->table('items')->select(['name'])->get();
328 |
329 | $this->assertTrue(is_array($results));
330 | $this->assertCount(1, $results);
331 | $this->assertEquals('foo', $results[0]->name);
332 |
333 | $result = $db->table('items')->first();
334 |
335 | $this->assertInstanceOf(stdClass::class, $result);
336 | $this->assertEquals('foo', $result->name);
337 |
338 | $result = $db->table('items')->last();
339 |
340 | $this->assertInstanceOf(stdClass::class, $result);
341 | $this->assertEquals('foo', $result->name);
342 | }
343 |
344 | public function testInsertRows()
345 | {
346 | $db = $this->prepareDatabase();
347 |
348 | $results = $db->table('items')->get();
349 |
350 | $this->assertTrue(is_array($results));
351 | $this->assertCount(1, $results);
352 |
353 | $db->table('items')->insert([
354 | ['name' => 'bar'],
355 | ['name' => 'baz'],
356 | ]);
357 |
358 | $results = $db->table('items')->get();
359 |
360 | $this->assertTrue(is_array($results));
361 | $this->assertCount(3, $results);
362 | }
363 |
364 | public function testUpdateRows()
365 | {
366 | $db = $this->prepareDatabase();
367 |
368 | $results = $db->table('items')->get();
369 |
370 | $this->assertTrue(is_array($results));
371 | $this->assertCount(1, $results);
372 | $this->assertEquals('foo', $results[0]->name);
373 |
374 | $db->table('items')->where('name', 'foo')->update(['name' => 'bar']);
375 |
376 | $results = $db->table('items')->get();
377 |
378 | $this->assertTrue(is_array($results));
379 | $this->assertCount(1, $results);
380 | $this->assertEquals('bar', $results[0]->name);
381 | }
382 |
383 | public function testDeleteRows()
384 | {
385 | $db = $this->prepareDatabase();
386 |
387 | $results = $db->table('items')->get();
388 |
389 | $this->assertTrue(is_array($results));
390 | $this->assertCount(1, $results);
391 |
392 | $db->table('items')->where('name', 'foo')->delete();
393 |
394 | $results = $db->table('items')->get();
395 |
396 | $this->assertTrue(is_array($results));
397 | $this->assertCount(0, $results);
398 | }
399 |
400 | public function testBuilderGrammars()
401 | {
402 | $db = $this->prepareDatabase();
403 |
404 | $this->assertEquals('SELECT * FROM `items`', $db->table('items')->toSql());
405 | $this->assertEquals('SELECT * FROM `items` WHERE `name` = "foo"', $db->table('items')->where('name', 'foo')->toSql());
406 | }
407 |
408 | public function testOrderByRandom()
409 | {
410 | $db = $this->prepareDatabase();
411 |
412 | $this->assertEquals(
413 | 'SELECT * FROM `items` ORDER BY RANDOM()',
414 | $db->table('items')->orderByRandom()->toSql()
415 | );
416 | }
417 |
418 | public function testSelectingColumnAsAlias()
419 | {
420 | $db = $this->prepareDatabase();
421 |
422 | $this->assertEquals(
423 | 'SELECT `foo` as `bar`, `toast` FROM `items`',
424 | $db->table('items')->select([['foo', 'bar'], 'toast'])->toSql()
425 | );
426 | }
427 |
428 | public function testOverwritingRepository()
429 | {
430 | $db = $this->prepareDatabase();
431 |
432 | $this->assertInstanceOf(ContainerRepository::class, $db->getRepository());
433 |
434 | $db->setRepository(new ArrayRepository([]));
435 |
436 | $this->assertInstanceOf(ArrayRepository::class, $db->getRepository());
437 | }
438 |
439 | public function testWhereStatements()
440 | {
441 | $db = $this->prepareDatabase();
442 |
443 | $this->assertEquals(
444 | 'SELECT * FROM `table` WHERE `foo` = "bar" AND `bar` = "baz"',
445 | $db->table('table')->where('foo', 'bar')->where('bar', 'baz')->toSql()
446 | );
447 | }
448 |
449 | protected function prepareDatabase()
450 | {
451 | $db = new Database(
452 | new ContainerRepository(
453 | new Container(),
454 | 'db.'
455 | )
456 | );
457 |
458 | $this->setDatabaseConnectionsAndDrivers($db);
459 | $db->setDefaultConnection('sqlite');
460 |
461 | $this->runDatabaseMigrations($db);
462 | $this->runDatabaseSeeder($db);
463 |
464 | return $db;
465 | }
466 |
467 | protected function runDatabaseMigrations(Database $db)
468 | {
469 | $db->statement('CREATE TABLE `items` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` VARCHAR(55))');
470 | }
471 |
472 | protected function runDatabaseSeeder(Database $db)
473 | {
474 | $db->statement("INSERT INTO `items` (`name`) VALUES ('foo')");
475 | }
476 | }
477 |
--------------------------------------------------------------------------------
/src/Fist/Database/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | =5.6.4"
19 | },
20 | "require-dev": {
21 | "phpunit/phpunit": "~5.4",
22 | "fist/testing": "self.version",
23 | "fist/container": "self.version"
24 | },
25 | "autoload": {
26 | "psr-4": {
27 | "Fist\\Facade\\": "src/"
28 | }
29 | },
30 | "extra": {
31 | "branch-alias": {
32 | "dev-master": "1.0-dev"
33 | }
34 | },
35 | "suggest": {
36 | "fist/container": "Required for using ContainerFacade"
37 | },
38 | "minimum-stability": "dev"
39 | }
40 |
--------------------------------------------------------------------------------
/src/Fist/Facade/license.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Fistlab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Fist/Facade/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 | ./tests
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Fist/Facade/src/ContainerFacade.php:
--------------------------------------------------------------------------------
1 | make(static::getFacadeAccessor());
13 | }
14 |
15 | public static function getContainerInstance()
16 | {
17 | return Container::getInstance();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Fist/Facade/src/ContainerFacadeInterface.php:
--------------------------------------------------------------------------------
1 | assertEquals('foo', ExampleFacade::foo());
13 | }
14 |
15 | public function testFacadeParsesArguments()
16 | {
17 | $this->assertEquals('bar', ExampleFacade::text('bar'));
18 | }
19 |
20 | /**
21 | * @expectedException Fist\Facade\InvalidArgumentException
22 | * @expectedExceptionMessage Method [notExistingMethod] does not exists.
23 | */
24 | public function testFacadeThrowsException()
25 | {
26 | ExampleFacade::notExistingMethod();
27 | }
28 |
29 | public function testContainerFacadeBuilds()
30 | {
31 | $container = new Container();
32 |
33 | Container::setInstance($container);
34 |
35 | $container->instance('example', new ExampleInstance());
36 |
37 | $this->assertEquals('foo', ExampleContainerFacade::foo());
38 | }
39 |
40 | /**
41 | * @expectedException ReflectionException
42 | * @expectedExceptionMessage Class example does not exist
43 | */
44 | public function testContainerFacadeThrowException()
45 | {
46 | $container = new Container();
47 |
48 | Container::setInstance($container);
49 |
50 | $this->assertEquals('foo', ExampleContainerFacade::foo());
51 | }
52 | }
53 |
54 | class ExampleInstance
55 | {
56 | public function foo()
57 | {
58 | return 'foo';
59 | }
60 |
61 | public function text($text)
62 | {
63 | return $text;
64 | }
65 | }
66 |
67 | class ExampleFacade extends Facade
68 | {
69 | public static function getFacadeInstance()
70 | {
71 | return new ExampleInstance();
72 | }
73 | }
74 |
75 | class ExampleContainerFacade extends ContainerFacade
76 | {
77 | public static function getFacadeAccessor()
78 | {
79 | return 'example';
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/Fist/Facade/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | =5.6.4"
19 | },
20 | "require-dev": {
21 | "phpunit/phpunit": "~5.4",
22 | "fist/testing": "self.version",
23 | "fist/container": "self.version"
24 | },
25 | "autoload": {
26 | "psr-4": {
27 | "Fist\\Repository\\": "src/"
28 | }
29 | },
30 | "extra": {
31 | "branch-alias": {
32 | "dev-master": "1.0-dev"
33 | }
34 | },
35 | "suggest": {
36 | "fist/container": "Required for using ContainerRepository"
37 | },
38 | "minimum-stability": "dev"
39 | }
40 |
--------------------------------------------------------------------------------
/src/Fist/Repository/license.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Fistlab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Fist/Repository/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 | ./tests
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Fist/Repository/src/ArrayRepository.php:
--------------------------------------------------------------------------------
1 | items = $items;
14 |
15 | $this->separator = $separator;
16 | }
17 |
18 | public function get($key, $default = null)
19 | {
20 | if ($this->has($key)) {
21 | if (! is_null($this->separator)) {
22 | return $this->getSeparated($key, $this->items);
23 | }
24 |
25 | return $this->items[$key];
26 | }
27 |
28 | return $default;
29 | }
30 |
31 | public function set($key, $value)
32 | {
33 | if (! is_null($this->separator)) {
34 | $this->setSeparated($key, $value, $this->items);
35 | } else {
36 | $this->items[$key] = $value;
37 | }
38 | }
39 |
40 | public function has($key)
41 | {
42 | if (! is_null($this->separator)) {
43 | return $this->hasSeparated($key, $this->items);
44 | }
45 |
46 | return isset($this->items[$key]);
47 | }
48 |
49 | public function all()
50 | {
51 | return $this->items;
52 | }
53 |
54 | protected function getSeparated($key, array &$items)
55 | {
56 | $parts = explode($this->separator, $key);
57 | $part = array_shift($parts);
58 |
59 | if (count($parts) == 0) {
60 | return $items[$part];
61 | }
62 |
63 | return $this->getSeparated(
64 | implode($this->separator, $parts),
65 | $items[$part]
66 | );
67 | }
68 |
69 | protected function setSeparated($key, $value, array &$items)
70 | {
71 | $parts = explode($this->separator, $key);
72 | $part = array_shift($parts);
73 |
74 | if (count($parts) == 0) {
75 | return $items[$part] = $value;
76 | }
77 |
78 | if (! isset($items[$part]) || ! is_array($items[$part])) {
79 | $items[$part] = [];
80 | }
81 |
82 | return $this->setSeparated(
83 | implode($this->separator, $parts),
84 | $value,
85 | $items[$part]
86 | );
87 | }
88 |
89 | protected function hasSeparated($key, array $items)
90 | {
91 | $parts = explode($this->separator, $key);
92 | $part = array_shift($parts);
93 |
94 | if (! isset($items[$part]) || count($parts) == 0) {
95 | return isset($items[$part]);
96 | }
97 |
98 | return $this->hasSeparated(
99 | implode($this->separator, $parts),
100 | $items[$part]
101 | );
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/Fist/Repository/src/ContainerRepository.php:
--------------------------------------------------------------------------------
1 | container = $container;
17 |
18 | $this->prefix = $prefix;
19 | }
20 |
21 | public function get($key, $default = null)
22 | {
23 | if ($this->has($key)) {
24 | return $this->container->make($this->prefix.$key);
25 | }
26 |
27 | return $default;
28 | }
29 |
30 | public function set($key, $value)
31 | {
32 | if ($value instanceof Closure) {
33 | // I'm still unsure whether or not to use 'singleton' over 'bind' in this ase.
34 | $this->container->bind($this->prefix.$key, $value);
35 | } else {
36 | $this->container->instance($this->prefix.$key, $value);
37 | }
38 | }
39 |
40 | public function has($key)
41 | {
42 | return $this->container->bound($this->prefix.$key);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Fist/Repository/src/RepositoryInterface.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(
14 | RepositoryInterface::class,
15 | new ArrayRepository()
16 | );
17 |
18 | $this->assertInstanceOf(
19 | RepositoryInterface::class,
20 | new ContainerRepository(
21 | new Container()
22 | )
23 | );
24 | }
25 |
26 | public function testArrayRepositoryGetter()
27 | {
28 | $array = [
29 | 'foo' => 'bar',
30 | 'bar' => 'baz',
31 | 'foobar' => [
32 | 'foo' => 'bar',
33 | ],
34 | ];
35 |
36 | $repository = new ArrayRepository($array);
37 |
38 | $this->assertEquals('bar', $repository->get('foo'));
39 | $this->assertEquals('baz', $repository->get('bar'));
40 | $this->assertEquals(null, $repository->get('baz'));
41 | $this->assertEquals('baz', $repository->get('baz', 'baz'));
42 | $this->assertEquals('bar', $repository->get('foobar.foo'));
43 | }
44 |
45 | public function testArrayRepositorySetter()
46 | {
47 | $array = [
48 | 'foo' => 'bar',
49 | 'bar' => 'baz',
50 | 'foobar' => [
51 | 'foo' => 'bar',
52 | ],
53 | ];
54 |
55 | $repository = new ArrayRepository($array);
56 |
57 | $this->assertEquals('bar', $repository->get('foo'));
58 |
59 | $repository->set('foo', 'foobar');
60 |
61 | $this->assertEquals('foobar', $repository->get('foo'));
62 |
63 | $this->assertEquals(null, $repository->get('baz'));
64 |
65 | $repository->set('baz', 'baz');
66 |
67 | $this->assertEquals('baz', $repository->get('baz'));
68 |
69 | $this->assertEquals('bar', $repository->get('foobar.foo'));
70 |
71 | $repository->set('foobar.foo', 'baz');
72 |
73 | $this->assertEquals('baz', $repository->get('foobar.foo'));
74 |
75 | $this->assertEquals(null, $repository->get('foobar.bar'));
76 |
77 | $repository->set('foobar.bar', 'baz');
78 |
79 | $this->assertEquals('baz', $repository->get('foobar.bar'));
80 | }
81 |
82 | public function testContainerRepositoryGetter()
83 | {
84 | $repository = new ContainerRepository(
85 | $container = new Container()
86 | );
87 |
88 | $container->instance('foo', 'bar');
89 | $container->instance('bar', 'baz');
90 | $container->instance('foobar.foo', 'bar');
91 |
92 | $this->assertEquals('bar', $repository->get('foo'));
93 | $this->assertEquals('baz', $repository->get('bar'));
94 | $this->assertEquals(null, $repository->get('baz'));
95 | $this->assertEquals('baz', $repository->get('baz', 'baz'));
96 | $this->assertEquals('bar', $repository->get('foobar.foo'));
97 | }
98 |
99 | public function testContainerRepositorySetter()
100 | {
101 | $repository = new ContainerRepository(
102 | $container = new Container()
103 | );
104 |
105 | $container->instance('foo', 'bar');
106 | $container->instance('bar', 'baz');
107 | $container->instance('foobar.foo', 'bar');
108 |
109 | $this->assertEquals('bar', $repository->get('foo'));
110 |
111 | $repository->set('foo', 'foobar');
112 |
113 | $this->assertEquals('foobar', $repository->get('foo'));
114 |
115 | $this->assertEquals(null, $repository->get('baz'));
116 |
117 | $repository->set('baz', 'baz');
118 |
119 | $this->assertEquals('baz', $repository->get('baz'));
120 |
121 | $this->assertEquals('bar', $repository->get('foobar.foo'));
122 |
123 | $repository->set('foobar.foo', 'baz');
124 |
125 | $this->assertEquals('baz', $repository->get('foobar.foo'));
126 |
127 | $this->assertEquals(null, $repository->get('foobar.bar'));
128 |
129 | $repository->set('foobar.bar', 'baz');
130 |
131 | $this->assertEquals('baz', $repository->get('foobar.bar'));
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/Fist/Repository/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | =5.6.4"
19 | },
20 | "require-dev": {
21 | "phpunit/phpunit": "~5.4",
22 | "fist/testing": "self.version"
23 | },
24 | "autoload": {
25 | "psr-4": {
26 | "Fist\\Routing\\": "src/"
27 | }
28 | },
29 | "extra": {
30 | "branch-alias": {
31 | "dev-master": "1.0-dev"
32 | }
33 | },
34 | "minimum-stability": "dev"
35 | }
36 |
--------------------------------------------------------------------------------
/src/Fist/Routing/license.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Fistlab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Fist/Routing/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 | ./tests
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Fist/Routing/src/MethodNotAllowedException.php:
--------------------------------------------------------------------------------
1 | methods = $methods;
22 |
23 | $this->uri = $uri;
24 |
25 | $this->closure = $closure;
26 | }
27 |
28 | public function getMethods()
29 | {
30 | return $this->methods;
31 | }
32 |
33 | public function hasMethod($method)
34 | {
35 | return in_array(strtolower($method), $this->methods);
36 | }
37 |
38 | public function matches($uri)
39 | {
40 | return (bool) preg_match("#{$this->generatePattern()}$#", $uri);
41 | }
42 |
43 | public function generatePattern()
44 | {
45 | if (is_null($this->pattern)) {
46 | $this->pattern = preg_replace_callback('~\{(.*?)\}~s', function ($pattern) {
47 | $content = $pattern[1];
48 |
49 | if (strpos($content, ':')) {
50 | $parts = explode(':', $content);
51 |
52 | return '('.$parts[1].')';
53 | }
54 |
55 | if (isset($this->regex[$content])) {
56 | return $this->regex[$content];
57 | }
58 |
59 | return '(.+)';
60 | }, $this->uri);
61 | }
62 |
63 | return $this->pattern;
64 | }
65 |
66 | public function where($name, $value)
67 | {
68 | $this->regex[$name] = $value;
69 | }
70 |
71 | public function callAction()
72 | {
73 | $action = $this->closure;
74 |
75 | return $action();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Fist/Routing/src/Router.php:
--------------------------------------------------------------------------------
1 | routes[] = $route;
18 |
19 | return $route;
20 | }
21 |
22 | public function get($uri, Closure $closure)
23 | {
24 | return $this->add(['get', 'head'], $uri, $closure);
25 | }
26 |
27 | public function post($uri, Closure $closure)
28 | {
29 | return $this->add(['post'], $uri, $closure);
30 | }
31 |
32 | public function put($uri, Closure $closure)
33 | {
34 | return $this->add(['put'], $uri, $closure);
35 | }
36 |
37 | public function patch($uri, Closure $closure)
38 | {
39 | return $this->add(['patch'], $uri, $closure);
40 | }
41 |
42 | public function delete($uri, Closure $closure)
43 | {
44 | return $this->add(['delete'], $uri, $closure);
45 | }
46 |
47 | public function head($uri, Closure $closure)
48 | {
49 | return $this->add(['head'], $uri, $closure);
50 | }
51 |
52 | public function connect($uri, Closure $closure)
53 | {
54 | return $this->add(['connect'], $uri, $closure);
55 | }
56 |
57 | public function options($uri, Closure $closure)
58 | {
59 | return $this->add(['options'], $uri, $closure);
60 | }
61 |
62 | public function trace($uri, Closure $closure)
63 | {
64 | return $this->add(['trace'], $uri, $closure);
65 | }
66 |
67 | public function any($uri, Closure $closure)
68 | {
69 | return $this->add(['get', 'post', 'put', 'patch', 'delete', 'head', 'connect', 'options', 'trace'], $uri, $closure);
70 | }
71 |
72 | protected function prepareQueryString($uri)
73 | {
74 | return rawurldecode(
75 | $this->stripQueryString($uri)
76 | );
77 | }
78 |
79 | protected function stripQueryString($uri)
80 | {
81 | // Strip query string (?foo=bar) and decode URI
82 | $pos = strpos($uri, '?');
83 | if ($pos !== false) {
84 | return substr($uri, 0, $pos);
85 | }
86 |
87 | return $uri;
88 | }
89 |
90 | public function dispatch($method = null, $uri = null)
91 | {
92 | if (is_null($method)) {
93 | $method = getenv('REQUEST_METHOD');
94 | }
95 |
96 | $method = strtolower($method);
97 |
98 | if (is_null($uri)) {
99 | $uri = getenv('REQUEST_URI');
100 | }
101 |
102 | $uri = $this->prepareQueryString($uri);
103 |
104 | $methodMismatch = false;
105 |
106 | foreach ($this->routes as $route) {
107 | if ($route->matches($uri)) {
108 | if ($route->hasMethod($method)) {
109 | return $route->callAction();
110 | }
111 |
112 | $methodMismatch = true;
113 | }
114 | }
115 |
116 | if ($methodMismatch) {
117 | throw new MethodNotAllowedException('Method not allowed.');
118 | }
119 |
120 | throw new NotFoundException('Route not found.');
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/Fist/Routing/tests/RoutingTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(Router::class, $router);
14 | }
15 |
16 | protected function addRoute(Router $router, $method, array $methods = null, $uri = 'foo', Closure $closure = null)
17 | {
18 | if (is_null($methods)) {
19 | if (strtolower($method) == 'get') {
20 | $methods = ['get', 'head'];
21 | } else {
22 | $methods = [$method];
23 | }
24 | }
25 |
26 | if (is_null($closure)) {
27 | $closure = function () {
28 | return 'foo';
29 | };
30 | }
31 |
32 | $route = $router->$method($uri, $closure);
33 |
34 | $this->assertEquals(array_map('strtolower', $methods), $route->getMethods());
35 |
36 | foreach ($methods as $m) {
37 | $this->assertTrue($route->hasMethod($m));
38 | }
39 |
40 | return $route;
41 | }
42 |
43 | public function testAddingGetRoute()
44 | {
45 | $this->addRoute(
46 | new Router(),
47 | 'get'
48 | );
49 | }
50 |
51 | public function testAddingPostRoute()
52 | {
53 | $this->addRoute(
54 | new Router(),
55 | 'post'
56 | );
57 | }
58 |
59 | public function testAddingPutRoute()
60 | {
61 | $this->addRoute(
62 | new Router(),
63 | 'put'
64 | );
65 | }
66 |
67 | public function testAddingPatchRoute()
68 | {
69 | $this->addRoute(
70 | $router = new Router(),
71 | 'patch'
72 | );
73 | }
74 |
75 | public function testAddingDeleteRoute()
76 | {
77 | $this->addRoute(
78 | new Router(),
79 | 'delete'
80 | );
81 | }
82 |
83 | public function testAddingHeadRoute()
84 | {
85 | $this->addRoute(
86 | new Router(),
87 | 'head'
88 | );
89 | }
90 |
91 | public function testAddingConnectRoute()
92 | {
93 | $this->addRoute(
94 | new Router(),
95 | 'connect'
96 | );
97 | }
98 |
99 | public function testAddingOptionsRoute()
100 | {
101 | $this->addRoute(
102 | new Router(),
103 | 'options'
104 | );
105 | }
106 |
107 | public function testAddingTraceRoute()
108 | {
109 | $this->addRoute(
110 | new Router(),
111 | 'trace'
112 | );
113 | }
114 |
115 | public function testAddingAnyRoute()
116 | {
117 | $this->addRoute(
118 | new Router(),
119 | 'any',
120 | ['get', 'post', 'put', 'patch', 'delete', 'head', 'connect', 'options', 'trace']
121 | );
122 | }
123 |
124 | public function testRouteMatches()
125 | {
126 | $this->addRoute(
127 | $router = new Router(),
128 | 'get'
129 | );
130 |
131 | $match = $router->dispatch('get', 'http://localhost/foo');
132 |
133 | $this->assertEquals('foo', $match);
134 | }
135 |
136 | public function testRouteMatchesUppercaseMethods()
137 | {
138 | $this->addRoute(
139 | $router = new Router(),
140 | 'get'
141 | );
142 |
143 | $match = $router->dispatch('GET', 'http://localhost/foo');
144 |
145 | $this->assertEquals('foo', $match);
146 |
147 | $this->addRoute(
148 | $router = new Router(),
149 | 'GET'
150 | );
151 |
152 | $match = $router->dispatch('get', 'http://localhost/foo');
153 |
154 | $this->assertEquals('foo', $match);
155 | }
156 |
157 | /**
158 | * @expectedException Fist\Routing\NotFoundException
159 | * @expectedExceptionMessage Route not found.
160 | */
161 | public function testRouteFailsMatchingUri()
162 | {
163 | $this->addRoute(
164 | $router = new Router(),
165 | 'get'
166 | );
167 |
168 | $router->dispatch('get', 'http://localhost/bar');
169 | }
170 |
171 | /**
172 | * @expectedException Fist\Routing\MethodNotAllowedException
173 | * @expectedExceptionMessage Method not allowed.
174 | */
175 | public function testRouteFailsMatchingMethod()
176 | {
177 | $this->addRoute(
178 | $router = new Router(),
179 | 'get'
180 | );
181 |
182 | $router->dispatch('post', 'http://localhost/foo');
183 | }
184 |
185 | public function testRouteWithSlashes()
186 | {
187 | $router = new Router();
188 |
189 | $this->addRoute($router, 'get', null, 'foo/bar/baz');
190 |
191 | $this->assertEquals('foo', $router->dispatch('get', 'http://localhost/foo/bar/baz'));
192 | }
193 |
194 | public function testRouteWithSimpleRegex()
195 | {
196 | $router = new Router();
197 |
198 | $this->addRoute($router, 'get', null, 'users/(.+)');
199 |
200 | $this->assertEquals('foo', $router->dispatch('get', 'http://localhost/users/mark'));
201 | }
202 |
203 | public function testRouteWithNamedValues()
204 | {
205 | $router = new Router();
206 |
207 | $this->addRoute($router, 'get', null, 'users/{name}');
208 |
209 | $this->assertEquals('foo', $router->dispatch('get', 'http://localhost/users/mark'));
210 | }
211 |
212 | public function testRouteWithMultipleNamedValues()
213 | {
214 | $router = new Router();
215 |
216 | $this->addRoute($router, 'get', null, 'users/{name}/{id}');
217 |
218 | $this->assertEquals('foo', $router->dispatch('get', 'http://localhost/users/mark/1'));
219 | }
220 |
221 | public function testRouteWithNamedRegex()
222 | {
223 | $router = new Router();
224 |
225 | $this->addRoute($router, 'get', null, 'users/{name:[0-9]}');
226 |
227 | $this->assertEquals('foo', $router->dispatch('get', 'http://localhost/users/1'));
228 | }
229 |
230 | /**
231 | * @expectedException Fist\Routing\NotFoundException
232 | * @expectedExceptionMessage Route not found.
233 | */
234 | public function testRouteWithNamedRegexFailsSinceItAllowsOnlyNumbers()
235 | {
236 | $router = new Router();
237 |
238 | $this->addRoute($router, 'get', null, 'users/{name:[0-9]}');
239 |
240 | $this->assertEquals('foo', $router->dispatch('get', 'http://localhost/users/mark'));
241 | }
242 |
243 | public function testRouteWithWhereStatements()
244 | {
245 | $router = new Router();
246 |
247 | $this->addRoute($router, 'get', null, 'users/{name}')
248 | ->where('name', '[0-9]');
249 |
250 | $this->assertEquals('foo', $router->dispatch('get', 'http://localhost/users/1'));
251 | }
252 |
253 | /**
254 | * @expectedException Fist\Routing\NotFoundException
255 | * @expectedExceptionMessage Route not found.
256 | */
257 | public function testRouteWithWhereStatementsFailsUsingString()
258 | {
259 | $router = new Router();
260 |
261 | $this->addRoute($router, 'get', null, 'users/{name}')
262 | ->where('name', '[0-9]');
263 |
264 | $this->assertEquals('foo', $router->dispatch('get', 'http://localhost/users/mark'));
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/src/Fist/Routing/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | =5.6.4"
19 | },
20 | "require-dev": {
21 | "phpunit/phpunit": "~5.4"
22 | },
23 | "autoload": {
24 | "psr-4": {
25 | "Fist\\Testing\\": "src/"
26 | }
27 | },
28 | "extra": {
29 | "branch-alias": {
30 | "dev-master": "1.0-dev"
31 | }
32 | },
33 | "minimum-stability": "dev"
34 | }
35 |
--------------------------------------------------------------------------------
/src/Fist/Testing/license.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Fistlab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Fist/Testing/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 | ./tests
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Fist/Testing/src/TestCase.php:
--------------------------------------------------------------------------------
1 | fail('Method did not throw exception as expected.');
17 | } catch (PHPUnit_Framework_AssertionFailedError $e) {
18 | throw $e;
19 | } catch (Exception $e) {
20 | if (! is_null($class)) {
21 | $this->assertEquals($class, get_class($e));
22 | }
23 |
24 | if (is_array($messages)) {
25 | $this->assertTrue(in_array($e->getMessage(), $messages));
26 | } elseif (! is_null($messages)) {
27 | $this->assertEquals($messages, $e->getMessage());
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Fist/Testing/src/WithDatabase.php:
--------------------------------------------------------------------------------
1 | testConnections as $connection => $values) {
13 | $this->setDatabaseConnection($database, $connection, $values);
14 |
15 | $driver = isset($values['driver']) ? $values['driver'] : null;
16 |
17 | $this->setDatabaseDriver($database, $driver);
18 | }
19 | }
20 |
21 | protected function setDatabaseConnection(Database $database, $name, $value = null)
22 | {
23 | if (is_null($value)) {
24 | $value = $this->testConnections[$name];
25 | }
26 |
27 | $database->setConnection($name, $value);
28 | }
29 |
30 | protected function setDatabaseDriver(Database $database, $name)
31 | {
32 | $class = 'Fist\\Database\\Connectors\\'.ucfirst($name).'Connection';
33 |
34 | $database->setDriver($name, $class);
35 | }
36 |
37 | protected function runOnDatabaseConnections(Database $database, array $connections, Closure $closure)
38 | {
39 | foreach ($connections as $connection) {
40 | $closure($database->connection($connection));
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Fist/Testing/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |