Completed
Push — master ( 747e00...006ae1 )
by Changwan
03:04
created

MysqlConnection::all()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 4
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
namespace Wandu\Database\Connection;
3
4
use Doctrine\Common\Annotations\Reader;
5
use Exception;
6
use ArrayAccess;
7
use PDO;
8
use PDOStatement;
9
use Throwable;
10
use Wandu\Collection\ArrayList;
11
use Wandu\Database\Contracts\ConnectionInterface;
12
use Wandu\Database\Contracts\QueryInterface;
13
use Wandu\Database\Exception\ClassNotFoundException;
14
use Wandu\Database\QueryBuilder;
15
use Wandu\Database\Repository\Repository;
16
use Wandu\Database\Repository\RepositorySettings;
17
18
class MysqlConnection implements ConnectionInterface
19
{
20
    /** @var \PDO */
21
    protected $pdo;
22
23
    /** @var \ArrayAccess */
24
    protected $container;
25
26
    /** @var string */
27
    protected $prefix;
28
29
    /**
30
     * @param \PDO $pdo
31
     * @param \ArrayAccess $container
32
     * @param string $prefix
33
     */
34 17
    public function __construct(PDO $pdo, ArrayAccess $container = null, $prefix = '')
35
    {
36 17
        $this->pdo = $pdo;
37 17
        $this->container = $container;
38 17
        $this->prefix = $prefix;
39 17
    }
40
41
    /**
42
     * {@inheritdoc}
43
     */
44 8
    public function getPrefix()
45
    {
46 8
        return $this->prefix;
47
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52 8
    public function createQueryBuilder($table)
53
    {
54 8
        return new QueryBuilder($this->getPrefix() . $table);
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60 14
    public function createRepository($className)
61
    {
62 14
        if (!$this->container || !$this->container[Reader::class]) {
63 1
            throw new ClassNotFoundException(Reader::class);
64
        }
65 13
        return new Repository($this, RepositorySettings::fromAnnotation($className, $this->container[Reader::class]));
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \Wandu\Databa...tions\Reader::class])); (Wandu\Database\Repository\Repository) is incompatible with the return type declared by the interface Wandu\Database\Contracts...rface::createRepository of type Wandu\Database\Contracts\RepositoryInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71 6
    public function fetch($query, array $bindings = [])
72
    {
73 6
        $statement = $this->prepare($query, $bindings);
74 6
        $statement->execute();
75 6
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
76 6
            yield $row;
77
        }
78 6
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83
    public function all($query, array $bindings = [])
84
    {
85
        return new ArrayList($this->fetch($query, $bindings));
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91 8
    public function first($query, array $bindings = [])
92
    {
93 8
        $statement = $this->prepare($query, $bindings);
94 8
        $statement->execute();
95 8
        $attributes = $statement->fetch(PDO::FETCH_ASSOC);
96 8
        return $attributes ?: null;
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102 2
    public function query($query, array $bindings = [])
103
    {
104 2
        $statement = $this->prepare($query, $bindings);
105 2
        $statement->execute();
106 2
        return $statement->rowCount();
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112 1
    public function getLastInsertId()
113
    {
114 1
        return $this->pdo->lastInsertId();
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120
    public function transaction(callable $handler)
121
    {
122
        $this->pdo->beginTransaction();
123
        try {
124
            call_user_func($handler, $this);
125
        } catch (Exception $e) {
126
            $this->pdo->rollBack();
127
            throw $e;
128
        } catch (Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
129
            $this->pdo->rollBack();
130
            throw $e;
131
        }
132
        $this->pdo->commit();
133
    }
134
135
    /**
136
     * @param string|callable|\Wandu\Database\Contracts\QueryInterface $query
137
     * @param array $bindings
138
     * @return \PDOStatement
139
     */
140 14
    protected function prepare($query, array $bindings = [])
141
    {
142 14
        while (is_callable($query)) {
143 2
            $query = call_user_func($query);
144
        }
145 14
        if ($query instanceof QueryInterface) {
146 8
            $bindings = $query->getBindings();
147 8
            $query = $query->toSql();
148
        }
149 14
        $statement = $this->pdo->prepare($query);
150 14
        $this->bindValues($statement, $bindings);
151 14
        return $statement;
152
    }
153
154
    /**
155
     * @param \PDOStatement $statement
156
     * @param array $bindings
157
     */
158 14
    protected function bindValues(PDOStatement $statement, array $bindings = [])
159
    {
160 14
        foreach ($bindings as $key => $value) {
161 14
            if (is_int($value)) {
162 7
                $dataType = PDO::PARAM_INT;
163 12
            } elseif (is_bool($value)) {
164
                $dataType = PDO::PARAM_BOOL;
165 12
            } elseif (is_null($value)) {
166
                $dataType = PDO::PARAM_NULL;
167
            } else {
168 12
                $dataType = PDO::PARAM_STR;
169
            }
170 14
            $statement->bindValue(
171 14
                is_int($key) ? $key + 1 : $key,
172
                $value,
173
                $dataType
174
            );
175
        }
176 14
    }
177
}
178