Completed
Push — master ( 1e376d...b0060a )
by Changwan
05:50
created

MysqlConnection   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 151
ccs 0
cts 91
cp 0
rs 10
c 0
b 0
f 0
wmc 23
lcom 2
cbo 6

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getPrefix() 0 4 1
A createQueryBuilder() 0 4 1
A createRepository() 0 7 3
A fetch() 0 8 2
A first() 0 6 1
A query() 0 6 1
A getLastInsertId() 0 4 1
A transaction() 0 14 3
A prepare() 0 13 3
B bindValues() 0 19 6
1
<?php
2
namespace Wandu\Database\Connection;
3
4
use Doctrine\Common\Annotations\Reader;
5
use Exception;
6
use Interop\Container\ContainerInterface;
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 \Interop\Container\ContainerInterface */
23
    protected $container;
24
25
    /** @var string */
26
    protected $prefix;
27
28
    /**
29
     * @param \PDO $pdo
30
     * @param \Interop\Container\ContainerInterface $container
31
     * @param string $prefix
32
     */
33
    public function __construct(PDO $pdo, ContainerInterface $container = null, $prefix = '')
34
    {
35
        $this->pdo = $pdo;
36
        $this->container = $container;
37
        $this->prefix = $prefix;
38
    }
39
40
    /**
41
     * {@inheritdoc}
42
     */
43
    public function getPrefix()
44
    {
45
        return $this->prefix;
46
    }
47
48
    /**
49
     * {@inheritdoc}
50
     */
51
    public function createQueryBuilder($table)
52
    {
53
        return new QueryBuilder($this->getPrefix() . $table);
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59
    public function createRepository($className)
60
    {
61
        if (!$this->container || !$this->container->has(Reader::class)) {
62
            throw new ClassNotFoundException(Reader::class);
63
        }
64
        return new Repository($this, RepositorySettings::fromAnnotation($className, $this->container->get(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
    public function fetch($query, array $bindings = [])
71
    {
72
        $statement = $this->prepare($query, $bindings);
73
        $statement->execute();
74
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
75
            yield $row;
76
        }
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function first($query, array $bindings = [])
83
    {
84
        $statement = $this->prepare($query, $bindings);
85
        $statement->execute();
86
        return $statement->fetch(PDO::FETCH_ASSOC);
87
    }
88
89
    /**
90
     * {@inheritdoc}
91
     */
92
    public function query($query, array $bindings = [])
93
    {
94
        $statement = $this->prepare($query, $bindings);
95
        $statement->execute();
96
        return $statement->rowCount();
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102
    public function getLastInsertId()
103
    {
104
        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
    protected function prepare($query, array $bindings = [])
131
    {
132
        while (is_callable($query)) {
133
            $query = call_user_func($query);
134
        }
135
        if ($query instanceof QueryInterface) {
136
            $bindings = $query->getBindings();
137
            $query = $query->toSql();
138
        }
139
        $statement = $this->pdo->prepare($query);
140
        $this->bindValues($statement, $bindings);
141
        return $statement;
142
    }
143
144
    /**
145
     * @param \PDOStatement $statement
146
     * @param array $bindings
147
     */
148
    protected function bindValues(PDOStatement $statement, array $bindings = [])
149
    {
150
        foreach ($bindings as $key => $value) {
151
            if (is_int($value)) {
152
                $dataType = PDO::PARAM_INT;
153
            } elseif (is_bool($value)) {
154
                $dataType = PDO::PARAM_BOOL;
155
            } elseif (is_null($value)) {
156
                $dataType = PDO::PARAM_NULL;
157
            } else {
158
                $dataType = PDO::PARAM_STR;
159
            }
160
            $statement->bindValue(
161
                is_int($key) ? $key + 1 : $key,
162
                $value,
163
                $dataType
164
            );
165
        }
166
    }
167
}
168