1 <?php
2 3 4 5 6 7 8 9 10
11
12 namespace Neevo;
13
14 use ArrayAccess;
15 use InvalidArgumentException;
16 use Neevo\Cache\MemoryStorage;
17 use Neevo\Cache\StorageInterface;
18 use Neevo\DriverInterface;
19 use PDO;
20 use ReflectionClass;
21 use ReflectionException;
22 use SplObjectStorage;
23 use Traversable;
24
25
26 27 28 29 30 31 32 33 34 35 36 37 38
39 class Connection implements ObservableInterface, ArrayAccess {
40
41
42
43 private $config;
44
45
46 private $connected = false;
47
48
49 private $driver;
50
51
52 private $parser = 'Neevo\\Parser';
53
54
55 private $observers;
56
57
58 private $cache;
59
60
61 62 63 64 65 66
67 public function __construct($config, StorageInterface $cache = null){
68 $this->observers = new SplObjectStorage;
69
70 $this->cache = $cache !== null ? $cache : new MemoryStorage;
71
72
73 if(is_string($config)){
74 parse_str($config, $config);
75 } elseif($config instanceof Traversable){
76 $tmp = array();
77 foreach($config as $key => $val){
78 $tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val;
79 }
80 $config = $tmp;
81 } elseif(class_exists('PDO') && $config instanceof PDO){
82 $config = array(
83 'driver' => 'pdo',
84 'pdo' => $config
85 );
86 } elseif(!is_array($config)){
87 throw new InvalidArgumentException('Configuration must be an array, string or Traversable.');
88 }
89
90
91 $defaults = array(
92 'driver' => Manager::$defaultDriver,
93 'lazy' => true,
94 'rowClass' => 'Neevo\\Row',
95 'tablePrefix' => '',
96 'result' => array(
97 'detectTypes' => false,
98 'formatDate' => '',
99 ),
100 );
101
102
103 self::alias($config, 'driver', 'extension');
104 self::alias($config, 'username', 'user');
105 self::alias($config, 'password', 'pass');
106 self::alias($config, 'password', 'pswd');
107 self::alias($config, 'host', 'hostname');
108 self::alias($config, 'host', 'server');
109 self::alias($config, 'database', 'db');
110 self::alias($config, 'database', 'dbname');
111 self::alias($config, 'tablePrefix', 'table_prefix');
112 self::alias($config, 'tablePrefix', 'prefix');
113 self::alias($config, 'charset', 'encoding');
114 self::alias($config, 'result.detectTypes', 'detectTypes');
115 self::alias($config, 'result.formatDate', 'formatDateTime');
116
117 $config = array_replace_recursive($defaults, $config);
118
119 $this->setDriver($config['driver']);
120
121 $config['lazy'] = (bool) $config['lazy'] && strtolower($config['lazy']) !== 'false';
122 $this->config = $config;
123
124 if($config['lazy'] === false)
125 $this->connect();
126 }
127
128
129 130 131
132 public function __destruct(){
133 try{
134 $this->driver->closeConnection();
135 } catch(ImplementationException $e){
136
137 }
138
139 $this->notifyObservers(ObserverInterface::DISCONNECT);
140 }
141
142
143 144 145
146 public function connect(){
147 if($this->connected !== false)
148 return;
149
150 $this->driver->connect($this->config);
151 $this->connected = true;
152 $this->notifyObservers(ObserverInterface::CONNECT);
153 }
154
155
156 157 158 159 160
161 public function getConfig($key = null){
162 if($key === null)
163 return $this->config;
164 return isset($this->config[$key]) ? $this->config[$key] : null;
165 }
166
167
168 169 170 171
172 public function getPrefix(){
173 return isset($this->config['tablePrefix']) ? $this->config['tablePrefix'] : '';
174 }
175
176
177 178 179 180
181 public function getDriver(){
182 return $this->driver;
183 }
184
185
186 187 188 189
190 public function getParser(){
191 return $this->parser;
192 }
193
194
195 196 197 198
199 public function getCache(){
200 return $this->cache;
201 }
202
203
204 205 206 207
208 public function setCache(StorageInterface $cache){
209 $this->cache = $cache;
210 }
211
212
213 214 215 216 217
218 public function attachObserver(ObserverInterface $observer, $event){
219 $this->observers->attach($observer, $event);
220 }
221
222
223 224 225 226
227 public function detachObserver(ObserverInterface $observer){
228 $this->observers->detach($observer);
229 }
230
231
232 233 234 235
236 public function notifyObservers($event){
237 foreach($this->observers as $observer){
238 if($event & $this->observers->getInfo())
239 $observer->updateStatus($this, $event);
240 }
241 }
242
243
244 245 246 247 248
249 public function offsetGet($key){
250 return $this->getConfig($key);
251 }
252
253
254 255 256 257 258
259 public function offsetExists($key){
260 return isset($this->config[$key]);
261 }
262
263
264
265 public function offsetSet($offset, $value){
266
267 }
268
269
270
271 public function offsetUnset($offset){
272
273 }
274
275
276 277 278 279 280 281
282 public static function alias(&$config, $key, $alias){
283 if(!isset($config[$alias]))
284 return;
285 $tmp = & $config;
286 foreach(explode('.', $key) as $key){
287 $tmp = & $tmp[$key];
288 }
289 if(!isset($tmp))
290 $tmp = $config[$alias];
291 }
292
293
294 295 296 297 298
299 protected function setDriver($driver){
300 if(strcasecmp($driver, 'sqlite') === 0)
301 $driver = 'SQLite2';
302
303 $class = "Neevo\\Drivers\\{$driver}Driver";
304
305 if(!class_exists($class)){
306 $file = __DIR__ . '/Drivers/' . strtolower($driver) . '.php';
307 if(!file_exists($file))
308 throw new DriverException("$driver driver file ($file) does not exist.");
309 if(is_readable($file))
310 include_once $file;
311 else
312 throw new DriverException("$driver driver file ($file) is not readable.");
313 }
314 if(!$this->isDriver($class))
315 throw new DriverException("Class '$class' is not a valid Neevo driver class.");
316
317 $this->driver = new $class;
318
319
320 if($this->isParser($class))
321 $this->parser = $class;
322 }
323
324
325 326 327 328 329
330 protected function isDriver($class){
331 try{
332 $reflection = new ReflectionClass($class);
333 return $reflection->implementsInterface('Neevo\\DriverInterface');
334 } catch(ReflectionException $e){
335 return false;
336 }
337 }
338
339
340 341 342 343 344
345 protected function isParser($class){
346 try{
347 $reflection = new ReflectionClass($class);
348 return $reflection->isSubclassOf('Neevo\\Parser');
349 } catch(ReflectionException $e){
350 return false;
351 }
352 }
353
354
355 }
356