Completed
Push — master ( 3deaa5...4c5cb4 )
by Changwan
06:31
created

MysqlConnection::createQueryBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
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\Database\Contracts\ConnectionInterface;
11
use Wandu\Database\Contracts\QueryInterface;
12
use Wandu\Database\Exception\ClassNotFoundException;
13
use Wandu\Database\QueryBuilder;
14
use Wandu\Database\Repository\Repository;
15
use Wandu\Database\Repository\RepositorySettings;
16
17
class MysqlConnection implements ConnectionInterface
18
{
19
    /** @var \PDO */
20
    protected $pdo;
21
22
    /** @var \ArrayAccess */
23
    protected $container;
24
25
    /** @var string */
26
    protected $prefix;
27
28
    /**
29
     * @param \PDO $pdo
30
     * @param \ArrayAccess $container
31
     * @param string $prefix
32
     */
33 16
    public function __construct(PDO $pdo, ArrayAccess $container = null, $prefix = '')
34
    {
35 16
        $this->pdo = $pdo;
36 16
        $this->container = $container;
37 16
        $this->prefix = $prefix;
38 16
    }
39
40
    /**
41
     * {@inheritdoc}
42
     */
43 7
    public function getPrefix()
44
    {
45 7
        return $this->prefix;
46
    }
47
48
    /**
49
     * {@inheritdoc}
50
     */
51 7
    public function createQueryBuilder($table)
52
    {
53 7
        return new QueryBuilder($this->getPrefix() . $table);
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59 13
    public function createRepository($className)
60
    {
61 13
        if (!$this->container || !$this->container[Reader::class]) {
62 1
            throw new ClassNotFoundException(Reader::class);
63
        }
64 12
        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...
65
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70 6
    public function fetch($query, array $bindings = [])
71
    {
72 6
        $statement = $this->prepare($query, $bindings);
73 6
        $statement->execute();
74 6
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
75 6
            yield $row;
76 6
        }
77 6
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82 7
    public function first($query, array $bindings = [])
83
    {
84 7
        $statement = $this->prepare($query, $bindings);
85 7
        $statement->execute();
86 7
        return $statement->fetch(PDO::FETCH_ASSOC);
87
    }
88
89
    /**
90
     * {@inheritdoc}
91
     */
92 2
    public function query($query, array $bindings = [])
93
    {
94 2
        $statement = $this->prepare($query, $bindings);
95 2
        $statement->execute();
96 2
        return $statement->rowCount();
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102 1
    public function getLastInsertId()
103
    {
104 1
        return $this->pdo->lastInsertId();
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function transaction(callable $handler)
111
    {
112
        $this->pdo->beginTransaction();
113
        try {
114
            call_user_func($handler, $this);
115
        } catch (Exception $e) {
116
            $this->pdo->rollBack();
117
            throw $e;
118
        } 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...
119
            $this->pdo->rollBack();
120
            throw $e;
121
        }
122
        $this->pdo->commit();
123
    }
124
125
    /**
126
     * @param string|callable|\Wandu\Database\Contracts\QueryInterface $query
127
     * @param array $bindings
128
     * @return \PDOStatement
129
     */
130 13
    protected function prepare($query, array $bindings = [])
131
    {
132 13
        while (is_callable($query)) {
133 2
            $query = call_user_func($query);
134 2
        }
135 13
        if ($query instanceof QueryInterface) {
136 7
            $bindings = $query->getBindings();
137 7
            $query = $query->toSql();
138 7
        }
139 13
        $statement = $this->pdo->prepare($query);
140 13
        $this->bindValues($statement, $bindings);
141 13
        return $statement;
142
    }
143
144
    /**
145
     * @param \PDOStatement $statement
146
     * @param array $bindings
147
     */
148 13
    protected function bindValues(PDOStatement $statement, array $bindings = [])
149
    {
150 13
        foreach ($bindings as $key => $value) {
151 13
            if (is_int($value)) {
152 6
                $dataType = PDO::PARAM_INT;
153 13
            } elseif (is_bool($value)) {
154
                $dataType = PDO::PARAM_BOOL;
155 12
            } elseif (is_null($value)) {
156 1
                $dataType = PDO::PARAM_NULL;
157 1
            } else {
158 12
                $dataType = PDO::PARAM_STR;
159
            }
160 13
            $statement->bindValue(
161 13
                is_int($key) ? $key + 1 : $key,
162 13
                $value,
163
                $dataType
164 13
            );
165 13
        }
166 13
    }
167
}
168