Completed
Push — master ( 5ab9f6...ab12c8 )
by Hong
07:19
created

Builder::union()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 2
nop 0
1
<?php
2
/**
3
 * Phossa Project
4
 *
5
 * PHP version 5.4
6
 *
7
 * @category  Library
8
 * @package   Phossa2\Query
9
 * @copyright Copyright (c) 2016 phossa.com
10
 * @license   http://mit-license.org/ MIT License
11
 * @link      http://www.phossa.com/
12
 */
13
/*# declare(strict_types=1); */
14
15
namespace Phossa2\Query;
16
17
use Phossa2\Query\Misc\Raw;
18
use Phossa2\Query\Dialect\Mysql;
19
use Phossa2\Query\Misc\Expression;
20
use Phossa2\Shared\Base\ObjectAbstract;
21
use Phossa2\Query\Traits\SettingsAwareTrait;
22
use Phossa2\Query\Traits\DialectAwareTrait;
23
use Phossa2\Query\Traits\ParameterAwareTrait;
24
use Phossa2\Query\Interfaces\BuilderInterface;
25
use Phossa2\Query\Interfaces\DialectInterface;
26
use Phossa2\Query\Interfaces\StatementInterface;
27
use Phossa2\Query\Interfaces\Statement\SelectStatementInterface;
28
use Phossa2\Query\Interfaces\Statement\InsertStatementInterface;
29
30
/**
31
 * Builder
32
 *
33
 * @package Phossa2\Query
34
 * @author  Hong Zhang <[email protected]>
35
 * @see     ObjectAbstract
36
 * @see     BuilderInterface
37
 * @see     StatementInterface
38
 * @see     SelectStatementInterface
39
 * @see     InsertStatementInterface
40
 * @version 2.0.0
41
 * @since   2.0.0 added
42
 */
43
class Builder extends ObjectAbstract implements BuilderInterface
44
{
45
    use DialectAwareTrait, SettingsAwareTrait, ParameterAwareTrait;
46
47
    /**
48
     * tables
49
     *
50
     * @var    array
51
     * @access protected
52
     */
53
    protected $tables = [];
54
55
    /**
56
     * Constructor
57
     *
58
     * ```php
59
     * // builder with default table `users` and Mysql dialect
60
     * $users = new Builder('users', new Mysql())
61
     *
62
     * // builder with defult tables:  `users` and `accounts` AS `a`
63
     * $builder = new Builder(['users', 'accounts' => 'a'])
64
     *
65
     * // change default settings
66
     * $builder = new Builder('users', new Mysql(), ['autoQuote' => false]);
67
     * ```
68
     *
69
     * @param  string|array $table table[s] to build upon
70
     * @param  DialectInterface $dialect default dialect is `Mysql`
71
     * @param  array $settings builder settings
72
     * @access public
73
     */
74
    public function __construct(
75
        $table = '',
76
        DialectInterface $dialect = null,
77
        array $settings = []
78
    ) {
79
        $this
80
            ->initParameter()
81
            ->setSettings(array_replace($this->defaultSettings(), $settings))
82
            ->setDialect($dialect)
83
            ->table($table);
84
    }
85
86
    /**
87
     * Change table[s]
88
     *
89
     * @param  $table change to table[s]
90
     * @return $this
91
     * @access public
92
     */
93
    public function __invoke($table)
94
    {
95
        return $this->table($table);
96
    }
97
98
    /**
99
     * {@inheritDoc}
100
     */
101
    public function expr()/*# : ExpressionInterface */
102
    {
103
        return new Expression($this);
104
    }
105
106
    /**
107
     * {@inheritDoc}
108
     */
109
    public function raw(/*# string */ $string)/*# : OutputInterface */
110
    {
111
        // values found
112
        if (func_num_args() > 1) {
113
            $values = func_get_arg(1);
114
            $string = $this->getParameter()
115
                ->replaceQuestionMark($string, $values);
116
        }
117
        return new Raw($string);
118
    }
119
120
    /**
121
     * If has existing tables, return a new instance with provided table[s]
122
     *
123
     * {@inheritDoc}
124
     */
125
    public function table($table, /*# string */ $alias = '')
126
    {
127
        $tbl = $this->fixTable($table, $alias);
128
        $clone = [] === $this->tables ? $this : clone $this;
129
        $clone->tables = $tbl;
130
        return $clone;
131
    }
132
133
    /**
134
     * Append to existing tables
135
     *
136
     * {@inheritDoc}
137
     */
138
    public function from($table, /*# string */ $alias = '')
139
    {
140
        $tbl = $this->fixTable($table, $alias);
141
        $this->tables = array_merge($this->tables, $tbl);
142
        return $this;
143
    }
144
145
    /**
146
     * {@inheritDoc}
147
     */
148
    public function select()/*# : SelectStatementInterface */
149
    {
150
        /* @var SelectStatementInterface $select */
151
        $select = $this->getDialect()->select($this);
152
        return $select->table($this->tables)->col(func_get_args());
153
    }
154
155
    /**
156
     * {@inheritDoc}
157
     */
158
    public function insert(array $values = [])/*# : InsertStatementInterface */
159
    {
160
        /* @var InsertStatementInterface $insert */
161
        $insert = $this->getDialect()->insert($this);
162
        return $insert->into(current($this->tables))->set($values);
163
    }
164
165
    /**
166
     * {@inheritDoc}
167
     */
168
    public function union()/*# : UnionStatementInterface */
169
    {
170
        /* @var UnionStatementInterface $union */
171
        $union = $this->getDialect()->union($this);
172
        if (func_num_args() > 0) { // acception variable parameters
173
            $args = func_get_args();
174
            foreach ($args as $arg) {
175
                $union->union($arg);
176
            }
177
        }
178
        return $union;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $union; (Phossa2\Query\UnionStatementInterface) is incompatible with the return type declared by the interface Phossa2\Query\Interfaces\BuilderInterface::union of type Phossa2\Query\Interfaces...UnionStatementInterface.

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...
179
    }
180
181
    /**
182
     * Convert to [$table => alias] or [$table]
183
     *
184
     * @param  string|string[] $table
185
     * @param  string $alias
186
     * @return array
187
     * @access protected
188
     */
189
    protected function fixTable(
190
        $table,
191
        /*# string */ $alias = ''
192
    )/*# : array */ {
193
        if (empty($table)) {
194
            $table = [];
195
        } elseif (!is_array($table)) {
196
            $table = empty($alias) ? [$table] : [$table => $alias];
197
        }
198
        return $table;
199
    }
200
201
    /**
202
     * Builder default settings
203
     *
204
     * @return array
205
     * @access protected
206
     */
207
    protected function defaultSettings()/*# : array */
208
    {
209
        return [
210
            'autoQuote' => true,
211
            'positionedParam' => false,
212
            'namedParam' => false,
213
            'seperator' => ' ',
214
            'indent' => '',
215
            'escapeFunction' => null,
216
            'useNullAsDefault' => false,
217
        ];
218
    }
219
}
220