Browse Source

Improved testability

Maurits van der Schee 10 years ago
parent
commit
1c4a271b3e
2 changed files with 84 additions and 64 deletions
  1. 72
    59
      api.php
  2. 12
    5
      test.php

+ 72
- 59
api.php View File

@@ -2,22 +2,19 @@
2 2
 
3 3
 class MySQL_CRUD_API {
4 4
 
5
-	public $method;
6
-	public $request;
7 5
 	public $mysqli;
8
-	public $database;
9
-	public $whitelist;
10
-	public $blacklist;
11 6
 
12
-	private function connectDatabase($hostname,$username,$password,$database) {
13
-		$mysqli = new mysqli($hostname,$username,$password,$database);
7
+	protected $config;
8
+
9
+	protected function connectDatabase($hostname,$username,$password,$database,$port,$socket) {
10
+		$mysqli = new mysqli($hostname,$username,$password,$database,$port,$socket);
14 11
 		if ($mysqli->connect_errno) {
15 12
 			throw new \Exception('Connect failed: '.$mysqli->connect_error);
16 13
 		}
17 14
 		return $mysqli;
18 15
 	}
19 16
 
20
-	private function mapMethodToAction($method,$request) {
17
+	protected function mapMethodToAction($method,$request) {
21 18
 		switch ($method) {
22 19
 			case 'GET': return count($request)>1?'read':'list';
23 20
 			case 'PUT': return 'update';
@@ -27,17 +24,17 @@ class MySQL_CRUD_API {
27 24
 		}
28 25
 	}
29 26
 
30
-	private function parseRequestParameter($request,$position,$characters,$default) {
27
+	protected function parseRequestParameter($request,$position,$characters,$default) {
31 28
 		$value = isset($request[$position])?$request[$position]:$default;
32 29
 		return $characters?preg_replace("/[^$characters]/",'',$value):$value;
33 30
 	}
34 31
 
35
-	private function parseGetParameter($name,$characters,$default) {
36
-		$value = isset($_GET[$name])?$_GET[$name]:$default;
32
+	protected function parseGetParameter($get,$name,$characters,$default) {
33
+		$value = isset($get[$name])?$get[$name]:$default;
37 34
 		return $characters?preg_replace("/[^$characters]/",'',$value):$value;
38 35
 	}
39 36
 
40
-	private function applyWhitelist($table,$action,$list) {
37
+	protected function applyWhitelist($table,$action,$list) {
41 38
 		if ($list===false) return $table;
42 39
 		$list = array_filter($list, function($actions) use ($action) {
43 40
 			return strpos($actions,$action[0])!==false;
@@ -45,7 +42,7 @@ class MySQL_CRUD_API {
45 42
 		return array_intersect($table, array_keys($list));
46 43
 	}
47 44
 
48
-	private function applyBlacklist($table,$action,$list) {
45
+	protected function applyBlacklist($table,$action,$list) {
49 46
 		if ($list===false) return $table;
50 47
 		$list = array_filter($list, function($actions) use ($action) {
51 48
 			return strpos($actions,$action[0])!==false;
@@ -53,14 +50,14 @@ class MySQL_CRUD_API {
53 50
 		return array_diff($table, array_keys($list));
54 51
 	}
55 52
 
56
-	private function applyWhitelistAndBlacklist($table, $action, $whitelist, $blacklist) {
53
+	protected function applyWhitelistAndBlacklist($table, $action, $whitelist, $blacklist) {
57 54
 		$table = $this->applyWhitelist($table, $action, $whitelist);
58 55
 		$table = $this->applyBlacklist($table, $action, $blacklist);
59 56
 		if (empty($table)) $this->exitWith404();
60 57
 		return $table;
61 58
 	}
62 59
 
63
-	private function processTableParameter($table,$database,$mysqli) {
60
+	protected function processTableParameter($table,$database,$mysqli) {
64 61
 		$tablelist = explode(',',$table);
65 62
 		$tables = array();
66 63
 		foreach ($tablelist as $table) {
@@ -73,7 +70,7 @@ class MySQL_CRUD_API {
73 70
 		return $tables;
74 71
 	}
75 72
 
76
-	private function findPrimaryKey($table,$database,$mysqli) {
73
+	protected function findPrimaryKey($table,$database,$mysqli) {
77 74
 		$keys = array();
78 75
 		if ($result = $mysqli->query("SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `COLUMN_KEY` = 'PRI' AND `TABLE_NAME` = '$table[0]' AND `TABLE_SCHEMA` = '$database'")) {
79 76
 			while ($row = $result->fetch_row()) $keys[] = $row[0];
@@ -82,7 +79,7 @@ class MySQL_CRUD_API {
82 79
 		return count($keys)?$keys[0]:false;
83 80
 	}
84 81
 
85
-	private function exitWith404() {
82
+	protected function exitWith404() {
86 83
 		if (isset($_SERVER['REQUEST_METHOD'])) {
87 84
 			die(header("Content-Type:",true,404));
88 85
 		} else {
@@ -90,7 +87,7 @@ class MySQL_CRUD_API {
90 87
 		}
91 88
 	}
92 89
 
93
-	private function startOutput($callback) {
90
+	protected function startOutput($callback) {
94 91
 		if (isset($_SERVER['REQUEST_METHOD'])) {
95 92
 			if ($callback) {
96 93
 				header("Content-Type: application/javascript");
@@ -101,13 +98,13 @@ class MySQL_CRUD_API {
101 98
 		}
102 99
 	}
103 100
 
104
-	private function endOutput($callback) {
101
+	protected function endOutput($callback) {
105 102
 		if ($callback) {
106 103
 			echo ');';
107 104
 		}
108 105
 	}
109 106
 
110
-	private function processKeyParameter($key,$table,$database,$mysqli) {
107
+	protected function processKeyParameter($key,$table,$database,$mysqli) {
111 108
 		if ($key) {
112 109
 			$key = array($key,$this->findPrimaryKey($table,$database,$mysqli));
113 110
 			if ($key[1]===false) $this->exitWith404();
@@ -115,7 +112,7 @@ class MySQL_CRUD_API {
115 112
 		return $key;
116 113
 	}
117 114
 
118
-	private function processOrderParameter($order,$table,$database,$mysqli) {
115
+	protected function processOrderParameter($order,$table,$database,$mysqli) {
119 116
 		if ($order) {
120 117
 			$order = explode(',',$order,2);
121 118
 			if (count($order)<2) $order[1]='ASC';
@@ -124,7 +121,7 @@ class MySQL_CRUD_API {
124 121
 		return $order;
125 122
 	}
126 123
 
127
-	private function processFilterParameter($filter,$match,$mysqli) {
124
+	protected function processFilterParameter($filter,$match,$mysqli) {
128 125
 		if ($filter) {
129 126
 			$filter = explode(':',$filter,2);
130 127
 			if (count($filter)==2) {
@@ -152,7 +149,7 @@ class MySQL_CRUD_API {
152 149
 		return $filter;
153 150
 	}
154 151
 
155
-	private function processPageParameter($page) {
152
+	protected function processPageParameter($page) {
156 153
 		if ($page) {
157 154
 			$page = explode(',',$page,2);
158 155
 			if (count($page)<2) $page[1]=20;
@@ -161,7 +158,7 @@ class MySQL_CRUD_API {
161 158
 		return $page;
162 159
 	}
163 160
 
164
-	private function retrieveObject($key,$table,$mysqli) {
161
+	protected function retrieveObject($key,$table,$mysqli) {
165 162
 		if (!$key) return false;
166 163
 		if ($result = $mysqli->query("SELECT * FROM `$table[0]` WHERE `$key[1]` = '$key[0]'")) {
167 164
 			$object = $result->fetch_assoc();
@@ -170,7 +167,7 @@ class MySQL_CRUD_API {
170 167
 		return $object;
171 168
 	}
172 169
 
173
-	private function createObject($input,$table,$mysqli) {
170
+	protected function createObject($input,$table,$mysqli) {
174 171
 		if (!$input) return false;
175 172
 		$keys = implode('`,`',array_map(function($v){ return preg_replace('/[^a-zA-Z0-9\-_]/','',$v); },array_keys((array)$input)));
176 173
 		$values = implode("','",array_map(function($v) use ($mysqli){ return $mysqli->real_escape_string($v); },array_values((array)$input)));
@@ -178,7 +175,7 @@ class MySQL_CRUD_API {
178 175
 		return $mysqli->insert_id;
179 176
 	}
180 177
 
181
-	private function updateObject($key,$input,$table,$mysqli) {
178
+	protected function updateObject($key,$input,$table,$mysqli) {
182 179
 		if (!$input) return false;
183 180
 		$sql = "UPDATE `$table[0]` SET ";
184 181
 		foreach (array_keys((array)$input) as $i=>$k) {
@@ -191,12 +188,12 @@ class MySQL_CRUD_API {
191 188
 		return $mysqli->affected_rows;
192 189
 	}
193 190
 
194
-	private function deleteObject($key,$table,$mysqli) {
191
+	protected function deleteObject($key,$table,$mysqli) {
195 192
 		$mysqli->query("DELETE FROM `$table[0]` WHERE `$key[1]`='$key[0]'");
196 193
 		return $mysqli->affected_rows;
197 194
 	}
198 195
 
199
-	private function findRelations($action,$table,$database,$mysqli) {
196
+	protected function findRelations($action,$table,$database,$mysqli) {
200 197
 		$collect = array();
201 198
 		$select = array();
202 199
 		if (count($table)>1) {
@@ -255,16 +252,17 @@ class MySQL_CRUD_API {
255 252
 		return array($collect,$select);
256 253
 	}
257 254
 
258
-	private function getParameters($method, $request, $database, $whitelist, $blacklist, $mysqli) {
255
+	protected function getParameters($config, $mysqli) {
256
+		extract($config);
259 257
 		$action    = $this->mapMethodToAction($method, $request);
260 258
 		$table     = $this->parseRequestParameter($request, 0, 'a-zA-Z0-9\-_*,', '*');
261 259
 		$key       = $this->parseRequestParameter($request, 1, 'a-zA-Z0-9\-,', false); // auto-increment or uuid
262
-		$callback  = $this->parseGetParameter('callback', 'a-zA-Z0-9\-_', false);
263
-		$page      = $this->parseGetParameter('page', '0-9,', false);
264
-		$filter    = $this->parseGetParameter('filter', false, 'exact');
265
-		$match     = $this->parseGetParameter('match', 'a-z', false);
266
-		$order     = $this->parseGetParameter('order', 'a-zA-Z0-9\-_*,', false);
267
-		$transform = $this->parseGetParameter('transform', '1', false);
260
+		$callback  = $this->parseGetParameter($get, 'callback', 'a-zA-Z0-9\-_', false);
261
+		$page      = $this->parseGetParameter($get, 'page', '0-9,', false);
262
+		$filter    = $this->parseGetParameter($get, 'filter', false, 'exact');
263
+		$match     = $this->parseGetParameter($get, 'match', 'a-z', false);
264
+		$order     = $this->parseGetParameter($get, 'order', 'a-zA-Z0-9\-_*,', false);
265
+		$transform = $this->parseGetParameter($get, 'transform', '1', false);
268 266
 
269 267
 		$table  = $this->processTableParameter($table,$database,$mysqli);
270 268
 		$key    = $this->processKeyParameter($key,$table,$database,$mysqli);
@@ -275,14 +273,14 @@ class MySQL_CRUD_API {
275 273
 		$table  = $this->applyWhitelistAndBlacklist($table,$action,$whitelist,$blacklist);
276 274
 
277 275
 		$object = $this->retrieveObject($key,$table,$mysqli);
278
-		$input  = json_decode(file_get_contents('php://input'));
276
+		$input  = json_decode(file_get_contents($post));
279 277
 
280 278
 		list($collect,$select) = $this->findRelations($action,$table,$database,$mysqli);
281 279
 
282 280
 		return compact('action','table','key','callback','page','filter','match','order','transform','mysqli','object','input','collect','select');
283 281
 	}
284 282
 
285
-	private function listCommand($parameters) {
283
+	protected function listCommand($parameters) {
286 284
 		extract($parameters);
287 285
 		$this->startOutput($callback);
288 286
 		echo '{';
@@ -378,7 +376,7 @@ class MySQL_CRUD_API {
378 376
 		$this->endOutput($callback);
379 377
 	}
380 378
 
381
-	private function readCommand($parameters) {
379
+	protected function readCommand($parameters) {
382 380
 		extract($parameters);
383 381
 		if (!$object) $this->exitWith404();
384 382
 		$this->startOutput($callback);
@@ -386,7 +384,7 @@ class MySQL_CRUD_API {
386 384
 		$this->endOutput($callback);
387 385
 	}
388 386
 
389
-	private function createCommand($parameters) {
387
+	protected function createCommand($parameters) {
390 388
 		extract($parameters);
391 389
 		if (!$input) $this->exitWith404();
392 390
 		$this->startOutput($callback);
@@ -394,7 +392,7 @@ class MySQL_CRUD_API {
394 392
 		$this->endOutput($callback);
395 393
 	}
396 394
 
397
-	private function updateCommand($parameters) {
395
+	protected function updateCommand($parameters) {
398 396
 		extract($parameters);
399 397
 		if (!$input) $this->exitWith404();
400 398
 		$this->startOutput($callback);
@@ -402,14 +400,14 @@ class MySQL_CRUD_API {
402 400
 		$this->endOutput($callback);
403 401
 	}
404 402
 
405
-	private function deleteCommand($parameters) {
403
+	protected function deleteCommand($parameters) {
406 404
 		extract($parameters);
407 405
 		$this->startOutput($callback);
408 406
 		echo json_encode($this->deleteObject($key,$table,$mysqli));
409 407
 		$this->endOutput($callback);
410 408
 	}
411 409
 
412
-	private function listCommandTransform($parameters) {
410
+	protected function listCommandTransform($parameters) {
413 411
 		if ($parameters['transform']) {
414 412
 			ob_start();
415 413
 		}
@@ -421,15 +419,33 @@ class MySQL_CRUD_API {
421 419
 		}
422 420
 	}
423 421
 
424
-	public function __construct($hostname,$username,$password,$database,$whitelist,$blacklist) {
425
-		$this->method = isset($_SERVER['REQUEST_METHOD'])?$_SERVER['REQUEST_METHOD']:'';
426
-		$this->request = explode('/', isset($_SERVER['PATH_INFO'])?trim($_SERVER['PATH_INFO'],'/'):'');
427
-		if ($hostname) {
428
-			$this->mysqli = $this->connectDatabase($hostname,$username,$password,$database);
422
+	public function __construct($parameters) {
423
+		extract($parameters);
424
+
425
+		$connect = isset($connect)?$connect:true;
426
+
427
+		$hostname = isset($hostname)?$hostname:null;
428
+		$username = isset($username)?$username:'root';
429
+		$password = isset($password)?$password:null;
430
+		$database = isset($database)?$database:'';
431
+		$port = isset($port)?$port:null;
432
+		$socket = isset($socket)?$socket:null;
433
+
434
+		$whitelist = isset($whitelist)?$whitelist:false;
435
+		$blacklist = isset($blacklist)?$blacklist:false;
436
+
437
+		$method = isset($method)?$method:$_SERVER['REQUEST_METHOD'];
438
+		$request = isset($request)?$request:$_SERVER['PATH_INFO'];
439
+		$get = isset($get)?$get:$_GET;
440
+		$post = isset($post)?$post:'php://input';
441
+
442
+		$request = explode('/', trim($request,'/'));
443
+
444
+		if ($connect) {
445
+			$this->mysqli = $this->connectDatabase($hostname,$username,$password,$database,$port,$socket);
429 446
 		}
430
-		$this->database = $database;
431
-		$this->whitelist = $whitelist;
432
-		$this->blacklist = $blacklist;
447
+
448
+		$this->config = compact('method', 'request', 'get', 'post', 'database', 'whitelist', 'blacklist');
433 449
 	}
434 450
 
435 451
 	public static function mysql_crud_api_transform(&$tables) {
@@ -466,7 +482,7 @@ class MySQL_CRUD_API {
466 482
 	}
467 483
 
468 484
 	public function executeCommand() {
469
-		$parameters = $this->getParameters($this->method, $this->request, $this->database, $this->whitelist, $this->blacklist, $this->mysqli);
485
+		$parameters = $this->getParameters($this->config, $this->mysqli);
470 486
 		switch($parameters['action']){
471 487
 			case 'list': $this->listCommandTransform($parameters); break;
472 488
 			case 'read': $this->readCommand($parameters); break;
@@ -480,13 +496,10 @@ class MySQL_CRUD_API {
480 496
 
481 497
 // only execute this when running in stand-alone mode
482 498
 if(count(get_required_files())<2) {
483
-	$api = new MySQL_CRUD_API(
484
-		"localhost",                        // hostname
485
-		"user",                             // username
486
-		"pass",                             // password
487
-		"db",                               // database
488
-		false,                              // whitelist
489
-		array("users"=>"crudl")             // blacklist
490
-	);
499
+	$api = new MySQL_CRUD_API(array(
500
+		'username'=>'xxx',
501
+		'password'=>'xxx',
502
+		'database'=>'xxx'
503
+	));
491 504
 	$api->executeCommand();
492 505
 }

+ 12
- 5
test.php View File

@@ -40,14 +40,21 @@ class MySQL_CRUD_API_Test extends PHPUnit_Framework_TestCase
40 40
 
41 41
 	private function expect($method,$url,$queries,$output)
42 42
 	{
43
-		$api = new MySQL_CRUD_API('','','','database',false,array("users"=>"crudl"));
44
-		$api->mysqli = $this->expectQueries($queries);
45
-		$api->method = "GET";
46 43
 		$url = explode('?',$url,2);
44
+		$request = $url[0];
45
+		$get = array();
47 46
 		if (isset($url[1])) {
48
-			parse_str($url[1],$_GET);
47
+			parse_str($url[1],$get);
49 48
 		}
50
-		$api->request = explode('/',ltrim($url[0],'/'));
49
+		$parameters = array(
50
+			'method' =>$method,
51
+			'request' =>$request,
52
+			'get' =>$get,
53
+			'connect'=>false,
54
+			'database'=>'database'
55
+		);
56
+		$api = new MySQL_CRUD_API($parameters);
57
+		$api->mysqli = $this->expectQueries($queries);
51 58
 		$this->expectOutputString($output);
52 59
 		$api->executeCommand();
53 60
 	}

Loading…
Cancel
Save