Completed
Push — master ( e6062f...aa5cfe )
by Vitaly
02:30
created

Database::innerFetchColumn()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 11
rs 9.4286
cc 2
eloc 6
nc 2
nop 2
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: egorov
5
 * Date: 09.05.2015
6
 * Time: 13:05
7
 */
8
namespace samsonframework\orm;
9
10
/**
11
 * Class Database
12
 * @package samsonframework\orm
13
 */
14
class Database implements DatabaseInterface
15
{
16
    /** @var \PDO Database driver */
17
    protected $driver;
18
19
    /** @var string Database name */
20
    protected $database;
21
22
    /** @var int Amount of milliseconds spent on queries */
23
    protected $elapsed;
24
25
    /** @var int Amount queries executed */
26
    protected $count;
27
28
    /** Do not serialize anything */
29
    public function __sleep()
30
    {
31
        return array();
32
    }
33
34
    /**
35
     * Connect to a database using driver with parameters
36
     * @param string $database Database name
37
     * @param string $username Database username
38
     * @param string $password Database password
39
     * @param string $host Database host(localhost by default)
40
     * @param int $port Database port(3306 by default)
41
     * @param string $driver Database driver for interaction(MySQL by default)
42
     * @param string $charset Database character set
43
     * @return bool True if connection to database was successful
44
     */
45
    public function connect(
46
        $database,
47
        $username,
48
        $password,
49
        $host = 'localhost',
50
        $port = 3306,
51
        $driver = 'mysql',
52
        $charset = 'utf8'
53
    ) {
54
        // If we have not connected yet
55
        if (!isset($this->driver)) {
56
            $this->database = $database;
57
58
            // Check if configured database exists
59
            $this->driver = new PDO($host, $database, $username, $password, $charset, $port, $driver);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \samsonframework\orm...harset, $port, $driver) of type object<samsonframework\orm\PDO> is incompatible with the declared type object<PDO> of property $driver.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
60
61
            // Set correct encodings
62
            $this->query("set character_set_client='utf8'");
0 ignored issues
show
Deprecated Code introduced by
The method samsonframework\orm\Database::query() has been deprecated with message: Use execute()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
63
            $this->query("set character_set_results='utf8'");
0 ignored issues
show
Deprecated Code introduced by
The method samsonframework\orm\Database::query() has been deprecated with message: Use execute()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
64
            $this->query("set collation_connection='utf8_general_ci'");
0 ignored issues
show
Deprecated Code introduced by
The method samsonframework\orm\Database::query() has been deprecated with message: Use execute()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
65
66
            //new ManagerGenerator($this);
67
        }
68
    }
69
70
    /**
71
     * Get database name
72
     * @return string
73
     */
74
    public function database()
75
    {
76
        return $this->database;
77
    }
78
79
    /**
80
     * High-level database query executor
81
     * @param string $sql SQL statement
82
     * @return mixed Database query result
83
     * @deprecated Use execute()
84
     */
85
    public function query($sql)
86
    {
87
        return $this->execute($sql);
88
    }
89
90
    /**
91
     * Intreal error beautifier
92
     * @param \Exception $exception
93
     * @param $sql
94
     */
95
    private function outputError(\Exception $exception, $sql, $text = 'Error executing database query:')
96
    {
97
        echo("\n" . '<div style="font-size:12px; position:relative; background:red; z-index:9999999;">'
98
            .'<div style="padding:4px 10px;">'.$text.'</div>'
99
            .'<div style="padding:0px 10px;">['.htmlspecialchars($exception->getMessage()).']</div>'
100
            .'<textarea style="display:block; width:100%; min-height:100px;">'.$sql . '</textarea></div>');
101
    }
102
103
    /**
104
     * Proxy function for executing database fetching logic with exception,
105
     * error, profile handling
106
     * @param callback $fetcher Callback for fetching
107
     * @return mixed Fetching function result
108
     */
109
    private function executeFetcher($fetcher, $sql)
110
    {
111
        $result = array();
112
113
        if (isset($this->driver)) {
114
            // Store timestamp
115
            $tsLast = microtime(true);
116
117
            try { // Call fetcher
118
                // Get argument and remove first one
119
                $args = func_get_args();
120
                array_shift($args);
121
122
                // Proxy calling of fetcher function with passing parameters
123
                $result = call_user_func_array($fetcher, $args);
124
            } catch (\PDOException $exception) {
125
                $this->outputError($exception, $sql, 'Error executing ['.$fetcher.']');
126
            }
127
128
            // Store queries count
129
            $this->count++;
130
131
            // Count elapsed time
132
            $this->elapsed += microtime(true) - $tsLast;
133
        }
134
135
        return $result;
136
    }
137
138
    /**
139
     * High-level database query executor
140
     * @param string $sql SQL statement
141
     * @return mixed Database query result
142
     */
143
    private function innerQuery($sql)
144
    {
145
        try {
146
            // Perform database query
147
            return $this->driver->prepare($sql)->execute();
148
        } catch (\PDOException $e) {
149
            $this->outputError($e, $sql);
150
        }
151
152
        return null;
153
    }
154
155
    /**
156
     * Retrieve array of records from a database, if $className is passed method
157
     * will try to create an object of that type. If request has failed than
158
     * method will return empty array of stdClass all arrays regarding to $className is
159
     * passed or not.
160
     *
161
     * @param string $sql SQL statement
162
     * @return array Collection of arrays or objects
163
     */
164
    private function innerFetch($sql, $className = null)
165
    {
166
        try {
167
            // Perform database query
168
            if (!isset($className)) { // Return array
169
                return $this->driver->query($sql)->fetchAll(\PDO::FETCH_ASSOC);
170
            } else { // Create object of passed class name
171
                return $this->driver->query($sql)->fetchAll(\PDO::FETCH_CLASS, $className, array(&$this));
172
            }
173
        } catch (\PDOException $e) {
174
            $this->outputError($e, $sql, 'Fetching database records:');
175
        }
176
177
        return array();
178
    }
179
180
    /**
181
     * Special accelerated function to retrieve db record fields instead of objects
182
     *
183
     * @param string $sql SQL statement
184
     * @param int $columnIndex Needed column index
185
     *
186
     * @return array Database records column value collection
187
     */
188
    private function innerFetchColumn($sql, $columnIndex)
189
    {
190
        try {
191
            // Perform database query
192
            return $this->driver->query($sql)->fetchAll(\PDO::FETCH_COLUMN, $columnIndex);
193
        } catch (\PDOException $e) {
194
            $this->outputError($e, $sql, 'Error fetching records column values:');
195
        }
196
197
        return array();
198
    }
199
200
    /**
201
     * High-level database query executor
202
     * @param string $sql SQL statement
203
     * @return mixed Database query result
204
     */
205
    public function execute($sql)
206
    {
207
        return $this->executeFetcher(array($this, 'innerQuery'), $sql);
208
    }
209
210
    /**
211
     * Retrieve array of records from a database, if $className is passed method
212
     * will try to create an object of that type. If request has failed than
213
     * method will return empty array of stdClass all arrays regarding to $className is
214
     * passed or not.
215
     *
216
     * @param string $sql SQL statement
217
     * @return array Collection of arrays or objects
218
     */
219
    public function fetch($sql)
220
    {
221
        return $this->executeFetcher(array($this, 'innerFetch'), $sql);
222
    }
223
224
    /**
225
     * Special accelerated function to retrieve db record fields instead of objects
226
     * TODO: Change to be independent of query and class name, just SQL, this SQL
227
     * should only have one column in SELECT part and then we do not need parameter
228
     * for this as we can always take 0.
229
     *
230
     * @param string $entity Entity identifier
231
     * @param QueryInterface Query object
232
     * @param string $field Entity field identifier
233
     *
234
     * @return array Collection of rows with field value
235
     */
236
    public function fetchColumn($entity, QueryInterface $query, $field)
237
    {
238
        // TODO: Remove old attributes retrieval
239
240
        return $this->executeFetcher(
241
            array($this, 'innerFetchColumn'),
242
            $this->prepareSQL($entity, $query),
243
            array_search($field, array_values($entity::$_table_attributes))
244
        );
245
    }
246
247
    /**
248
     * Count resulting rows.
249
     *
250
     * @param string Entity identifier
251
     * @param QueryInterface Query object
252
     *
253
     * @return int Amount of rows
254
     */
255
    public function count($entity, QueryInterface $query)
256
    {
257
        // Modify query SQL and add counting
258
        $result = $this->fetch('SELECT Count(*) as __Count FROM (' . $this->prepareSQL($entity, $query) . ') as __table');
259
260
        return isset($result[0]) ? $result[0]['__Count'] : 0;
261
    }
262
263
    /**
264
     * Quote variable for security reasons.
265
     *
266
     * @param string $value
267
     * @return string Quoted value
268
     */
269
    protected function quote($value)
270
    {
271
        return $this->driver->quote($value);
272
    }
273
274
    /**
275
     * Convert QueryInterface into SQL statement.
276
     *
277
     * @param string Entity identifier
278
     * @param QueryInterface Query object
279
     *
280
     * @return string SQL statement
281
     */
282
    protected function prepareSQL($entity, QueryInterface $query)
283
    {
284
285
    }
286
}
287