Completed
Push — intermixed-snippets ( 1a61e3 )
by Paul
01:52
created

QueryFactory::setLastInsertIdNames()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 *
4
 * This file is part of Aura for PHP.
5
 *
6
 * @license http://opensource.org/licenses/mit-license.php MIT
7
 *
8
 */
9
namespace Aura\SqlQuery;
10
11
/**
12
 *
13
 * Creates query statement objects.
14
 *
15
 * @package Aura.SqlQuery
16
 *
17
 */
18
class QueryFactory
19
{
20
    /**
21
     * Use the 'common' driver instead of a database-specific one.
22
     */
23
    const COMMON = 'common';
24
25
    /**
26
     *
27
     * What database are we building for?
28
     *
29
     * @param string
30
     *
31
     */
32
    protected $db;
33
34
    /**
35
     *
36
     * Build "common" query objects regardless of database type?
37
     *
38
     * @param bool
39
     *
40
     */
41
    protected $common = false;
42
43
    /**
44
     *
45
     * A map of `table.col` names to last-insert-id names.
46
     *
47
     * @var array
48
     *
49
     */
50
    protected $last_insert_id_names = array();
51
52
    /**
53
     *
54
     * A Quoter for identifiers.
55
     *
56
     * @param QuoterInterface
57
     *
58
     */
59
    protected $quoter;
60
61
    protected $instance_count = 0;
62
63
    /**
64
     *
65
     * Constructor.
66
     *
67
     * @param string $db The database type.
68
     *
69
     * @param string $common Pass the constant self::COMMON to force common
70
     * query objects instead of db-specific ones.
71
     *
72
     */
73 417
    public function __construct($db, $common = null)
74
    {
75 417
        $this->db = ucfirst(strtolower($db));
76 417
        $this->common = ($common === self::COMMON);
77 417
    }
78
79
    /**
80
     *
81
     * Sets the last-insert-id names to be used for Insert queries..
82
     *
83
     * @param array $last_insert_id_names A map of `table.col` names to
84
     * last-insert-id names.
85
     *
86
     * @return null
87
     *
88
     */
89 67
    public function setLastInsertIdNames(array $last_insert_id_names)
90
    {
91 67
        $this->last_insert_id_names = $last_insert_id_names;
92 67
    }
93
94
    /**
95
     *
96
     * Returns a new SELECT object.
97
     *
98
     * @return Common\SelectInterface
99
     *
100
     */
101 274
    public function newSelect()
102
    {
103 274
        return $this->newInstance('Select');
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->newInstance('Select'); (Aura\SqlQuery\Common\Sel...\Common\DeleteInterface) is incompatible with the return type documented by Aura\SqlQuery\QueryFactory::newSelect of type Aura\SqlQuery\Common\SelectInterface.

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...
104
    }
105
106
    /**
107
     *
108
     * Returns a new INSERT object.
109
     *
110
     * @return Common\InsertInterface
111
     *
112
     */
113 77
    public function newInsert()
114
    {
115 77
        $insert = $this->newInstance('Insert');
116 77
        $insert->setLastInsertIdNames($this->last_insert_id_names);
0 ignored issues
show
Bug introduced by
The method setLastInsertIdNames does only exist in Aura\SqlQuery\Common\InsertInterface, but not in Aura\SqlQuery\Common\Del...\Common\UpdateInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
117 77
        return $insert;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $insert; (Aura\SqlQuery\Common\Sel...\Common\DeleteInterface) is incompatible with the return type documented by Aura\SqlQuery\QueryFactory::newInsert of type Aura\SqlQuery\Common\InsertInterface.

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...
118
    }
119
120
    /**
121
     *
122
     * Returns a new UPDATE object.
123
     *
124
     * @return Common\UpdateInterface
125
     *
126
     */
127 38
    public function newUpdate()
128
    {
129 38
        return $this->newInstance('Update');
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->newInstance('Update'); (Aura\SqlQuery\Common\Sel...\Common\DeleteInterface) is incompatible with the return type documented by Aura\SqlQuery\QueryFactory::newUpdate of type Aura\SqlQuery\Common\UpdateInterface.

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...
130
    }
131
132
    /**
133
     *
134
     * Returns a new DELETE object.
135
     *
136
     * @return Common\DeleteInterface
137
     *
138
     */
139 28
    public function newDelete()
140
    {
141 28
        return $this->newInstance('Delete');
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->newInstance('Delete'); (Aura\SqlQuery\Common\Sel...\Common\DeleteInterface) is incompatible with the return type documented by Aura\SqlQuery\QueryFactory::newDelete of type Aura\SqlQuery\Common\DeleteInterface.

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...
142
    }
143
144
    /**
145
     *
146
     * Returns a new query object.
147
     *
148
     * @param string $query The query object type.
149
     *
150
     * @return Common\SelectInterface|Common\InsertInterface|Common\UpdateInterface|Common\DeleteInterface
151
     *
152
     */
153 417
    protected function newInstance($query)
154
    {
155 417
        $queryClass = "Aura\SqlQuery\\{$this->db}\\{$query}";
156 417
        if ($this->common) {
157 20
            $queryClass = "Aura\SqlQuery\Common\\{$query}";
158 20
        }
159
160 417
        $builderClass = "Aura\SqlQuery\\{$this->db}\\{$query}Builder";
161 417
        if ($this->common || ! class_exists($builderClass)) {
162 254
            $builderClass = "Aura\SqlQuery\Common\\{$query}Builder";
0 ignored issues
show
Unused Code introduced by
$builderClass is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
163 254
        }
164
165
166 417
        return new $queryClass(
167 417
            $this->getQuoter(),
168 417
            $this->newBuilder($query),
169 417
            $this->getSeqBindPrefix()
170 417
        );
171
    }
172
173 417
    protected function getSeqBindPrefix()
174
    {
175 417
        $seq_bind_prefix = '';
176 417
        if ($this->instance_count > 0) {
177 20
            $seq_bind_prefix = '_' . $this->instance_count;
178 20
        }
179 417
        $this->instance_count ++;
180 417
        return $seq_bind_prefix;
181
    }
182
183
    /**
184
     *
185
     * Returns a new Builder for the database driver.
186
     *
187
     * @param string $query The query type.
188
     *
189
     * @return AbstractBuilder
190
     *
191
     */
192 417
    protected function newBuilder($query)
193
    {
194 417
        $builderClass = "Aura\SqlQuery\\{$this->db}\\{$query}Builder";
195 417
        if ($this->common || ! class_exists($builderClass)) {
196 254
            $builderClass = "Aura\SqlQuery\Common\\{$query}Builder";
197 254
        }
198 417
        return new $builderClass();
199
    }
200
201
    /**
202
     *
203
     * Returns the Quoter object for queries; creates one if needed.
204
     *
205
     * @return Quoter
206
     *
207
     */
208 417
    protected function getQuoter()
209
    {
210 417
        if (! $this->quoter) {
211 417
            $this->quoter = $this->newQuoter();
212 417
        }
213 417
        return $this->quoter;
214
    }
215
216
    /**
217
     *
218
     * Returns a new Quoter for the database driver.
219
     *
220
     * @return QuoterInerface
221
     *
222
     */
223 417
    protected function newQuoter()
224
    {
225 417
        $quoterClass = "Aura\SqlQuery\\{$this->db}\Quoter";
226 417
        if (! class_exists($quoterClass)) {
227 168
            $quoterClass = "Aura\SqlQuery\Common\Quoter";
228 168
        }
229 417
        return new $quoterClass();
230
    }
231
}
232