2158 lines
		
	
	
	
		
			70 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			2158 lines
		
	
	
	
		
			70 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| //var_dump($_SERVER['REQUEST_METHOD'],$_SERVER['PATH_INFO']); die();
 | |
| 
 | |
| interface DatabaseInterface {
 | |
| 	public function getSql($name);
 | |
| 	public function connect($hostname,$username,$password,$database,$port,$socket,$charset);
 | |
| 	public function query($sql,$params);
 | |
| 	public function fetchAssoc($result);
 | |
| 	public function fetchRow($result);
 | |
| 	public function insertId($result);
 | |
| 	public function affectedRows($result);
 | |
| 	public function close($result);
 | |
| 	public function fetchFields($table);
 | |
| 	public function addLimitToSql($sql,$limit,$offset);
 | |
| 	public function likeEscape($string);
 | |
| 	public function isBinaryType($field);
 | |
| 	public function isGeometryType($field);
 | |
| 	public function getDefaultCharset();
 | |
| }
 | |
| 
 | |
| class MySQL implements DatabaseInterface {
 | |
| 
 | |
| 	protected $db;
 | |
| 	protected $queries;
 | |
| 
 | |
| 	public function __construct() {
 | |
| 		$this->queries = array(
 | |
| 			'list_tables'=>'SELECT
 | |
| 					"TABLE_NAME","TABLE_COMMENT"
 | |
| 				FROM
 | |
| 					"INFORMATION_SCHEMA"."TABLES"
 | |
| 				WHERE
 | |
| 					"TABLE_SCHEMA" = ?',
 | |
| 			'reflect_table'=>'SELECT
 | |
| 					"TABLE_NAME"
 | |
| 				FROM
 | |
| 					"INFORMATION_SCHEMA"."TABLES"
 | |
| 				WHERE
 | |
| 					"TABLE_NAME" COLLATE \'utf8_bin\' = ? AND
 | |
| 					"TABLE_SCHEMA" = ?',
 | |
| 			'reflect_pk'=>'SELECT
 | |
| 					"COLUMN_NAME"
 | |
| 				FROM
 | |
| 					"INFORMATION_SCHEMA"."COLUMNS"
 | |
| 				WHERE
 | |
| 					"COLUMN_KEY" = \'PRI\' AND
 | |
| 					"TABLE_NAME" = ? AND
 | |
| 					"TABLE_SCHEMA" = ?',
 | |
| 			'reflect_belongs_to'=>'SELECT
 | |
| 					"TABLE_NAME","COLUMN_NAME",
 | |
| 					"REFERENCED_TABLE_NAME","REFERENCED_COLUMN_NAME"
 | |
| 				FROM
 | |
| 					"INFORMATION_SCHEMA"."KEY_COLUMN_USAGE"
 | |
| 				WHERE
 | |
| 					"TABLE_NAME" COLLATE \'utf8_bin\' = ? AND
 | |
| 					"REFERENCED_TABLE_NAME" COLLATE \'utf8_bin\' IN ? AND
 | |
| 					"TABLE_SCHEMA" = ? AND
 | |
| 					"REFERENCED_TABLE_SCHEMA" = ?',
 | |
| 			'reflect_has_many'=>'SELECT
 | |
| 					"TABLE_NAME","COLUMN_NAME",
 | |
| 					"REFERENCED_TABLE_NAME","REFERENCED_COLUMN_NAME"
 | |
| 				FROM
 | |
| 					"INFORMATION_SCHEMA"."KEY_COLUMN_USAGE"
 | |
| 				WHERE
 | |
| 					"TABLE_NAME" COLLATE \'utf8_bin\' IN ? AND
 | |
| 					"REFERENCED_TABLE_NAME" COLLATE \'utf8_bin\' = ? AND
 | |
| 					"TABLE_SCHEMA" = ? AND
 | |
| 					"REFERENCED_TABLE_SCHEMA" = ?',
 | |
| 			'reflect_habtm'=>'SELECT
 | |
| 					k1."TABLE_NAME", k1."COLUMN_NAME",
 | |
| 					k1."REFERENCED_TABLE_NAME", k1."REFERENCED_COLUMN_NAME",
 | |
| 					k2."TABLE_NAME", k2."COLUMN_NAME",
 | |
| 					k2."REFERENCED_TABLE_NAME", k2."REFERENCED_COLUMN_NAME"
 | |
| 				FROM
 | |
| 					"INFORMATION_SCHEMA"."KEY_COLUMN_USAGE" k1,
 | |
| 					"INFORMATION_SCHEMA"."KEY_COLUMN_USAGE" k2
 | |
| 				WHERE
 | |
| 					k1."TABLE_SCHEMA" = ? AND
 | |
| 					k2."TABLE_SCHEMA" = ? AND
 | |
| 					k1."REFERENCED_TABLE_SCHEMA" = ? AND
 | |
| 					k2."REFERENCED_TABLE_SCHEMA" = ? AND
 | |
| 					k1."TABLE_NAME" COLLATE \'utf8_bin\' = k2."TABLE_NAME" COLLATE \'utf8_bin\' AND
 | |
| 					k1."REFERENCED_TABLE_NAME" COLLATE \'utf8_bin\' = ? AND
 | |
| 					k2."REFERENCED_TABLE_NAME" COLLATE \'utf8_bin\' IN ?'
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	public function getSql($name) {
 | |
| 		return isset($this->queries[$name])?$this->queries[$name]:false;
 | |
| 	}
 | |
| 
 | |
| 	public function connect($hostname,$username,$password,$database,$port,$socket,$charset) {
 | |
| 		$db = mysqli_connect($hostname,$username,$password,$database,$port,$socket);
 | |
| 		if (mysqli_connect_errno()) {
 | |
| 			throw new \Exception('Connect failed. '.mysqli_connect_error());
 | |
| 		}
 | |
| 		if (!mysqli_set_charset($db,$charset)) {
 | |
| 			throw new \Exception('Error setting charset. '.mysqli_error($db));
 | |
| 		}
 | |
| 		if (!mysqli_query($db,'SET SESSION sql_mode = \'ANSI_QUOTES\';')) {
 | |
| 			throw new \Exception('Error setting ANSI quotes. '.mysqli_error($db));
 | |
| 		}
 | |
| 		$this->db = $db;
 | |
| 	}
 | |
| 
 | |
| 	public function query($sql,$params) {
 | |
| 		$db = $this->db;
 | |
| 		$sql = preg_replace_callback('/\!|\?/', function ($matches) use (&$db,&$params) {
 | |
| 			$param = array_shift($params);
 | |
| 			if ($matches[0]=='!') {
 | |
| 				$key = preg_replace('/[^a-zA-Z0-9\-_=<> ]/','',is_object($param)?$param->key:$param);
 | |
| 				if (is_object($param) && $param->type=='base64') {
 | |
| 					return "TO_BASE64(\"$key\") as \"$key\"";
 | |
| 				}
 | |
| 				if (is_object($param) && $param->type=='wkt') {
 | |
| 					return "ST_AsText(\"$key\") as \"$key\"";
 | |
| 				}
 | |
| 				return '"'.$key.'"';
 | |
| 			} else {
 | |
| 				if (is_array($param)) return '('.implode(',',array_map(function($v) use (&$db) {
 | |
| 					return "'".mysqli_real_escape_string($db,$v)."'";
 | |
| 				},$param)).')';
 | |
| 				if (is_object($param) && $param->type=='base64') {
 | |
| 					return "x'".bin2hex(base64_decode($param->value))."'";
 | |
| 				}
 | |
| 				if (is_object($param) && $param->type=='wkt') {
 | |
| 					return "ST_GeomFromText('".mysqli_real_escape_string($db,$param->value)."')";
 | |
| 				}
 | |
| 				if ($param===null) return 'NULL';
 | |
| 				return "'".mysqli_real_escape_string($db,$param)."'";
 | |
| 			}
 | |
| 		}, $sql);
 | |
| 		//if (!strpos($sql,'INFORMATION_SCHEMA'))	echo "\n$sql\n";
 | |
| 		return mysqli_query($db,$sql);
 | |
| 	}
 | |
| 
 | |
| 	public function fetchAssoc($result) {
 | |
| 		return mysqli_fetch_assoc($result);
 | |
| 	}
 | |
| 
 | |
| 	public function fetchRow($result) {
 | |
| 		return mysqli_fetch_row($result);
 | |
| 	}
 | |
| 
 | |
| 	public function insertId($result) {
 | |
| 		return mysqli_insert_id($this->db);
 | |
| 	}
 | |
| 
 | |
| 	public function affectedRows($result) {
 | |
| 		return mysqli_affected_rows($this->db);
 | |
| 	}
 | |
| 
 | |
| 	public function close($result) {
 | |
| 		return mysqli_free_result($result);
 | |
| 	}
 | |
| 
 | |
| 	public function fetchFields($table) {
 | |
| 		$result = $this->query('SELECT * FROM ! WHERE 1=2;',array($table));
 | |
| 		return mysqli_fetch_fields($result);
 | |
| 	}
 | |
| 
 | |
| 	public function addLimitToSql($sql,$limit,$offset) {
 | |
| 		return "$sql LIMIT $limit OFFSET $offset";
 | |
| 	}
 | |
| 
 | |
| 	public function likeEscape($string) {
 | |
| 		return addcslashes($string,'%_');
 | |
| 	}
 | |
| 
 | |
| 	public function convertFilter($field, $comparator, $value) {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	public function isBinaryType($field) {
 | |
| 		//echo "$field->name: $field->type ($field->flags)\n";
 | |
| 		return (($field->flags & 128) && ($field->type>=249) && ($field->type<=252));
 | |
| 	}
 | |
| 
 | |
| 	public function isGeometryType($field) {
 | |
| 		return ($field->type==255);
 | |
| 	}
 | |
| 
 | |
| 	public function getDefaultCharset() {
 | |
| 		return 'utf8';
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| class PostgreSQL implements DatabaseInterface {
 | |
| 
 | |
| 	protected $db;
 | |
| 	protected $queries;
 | |
| 
 | |
| 	public function __construct() {
 | |
| 		$this->queries = array(
 | |
| 			'list_tables'=>'select
 | |
| 					"table_name","table_comment"
 | |
| 				from
 | |
| 					"information_schema"."tables"
 | |
| 				where
 | |
| 					"table_catalog" = ?',
 | |
| 			'reflect_table'=>'select
 | |
| 					"table_name"
 | |
| 				from
 | |
| 					"information_schema"."tables"
 | |
| 				where
 | |
| 					"table_name" like ? and
 | |
| 					"table_catalog" = ?',
 | |
| 			'reflect_pk'=>'select
 | |
| 					"column_name"
 | |
| 				from
 | |
| 					"information_schema"."table_constraints" tc,
 | |
| 					"information_schema"."key_column_usage" ku
 | |
| 				where
 | |
| 					tc."constraint_type" = \'PRIMARY KEY\' and
 | |
| 					tc."constraint_name" = ku."constraint_name" and
 | |
| 					ku."table_name" = ? and
 | |
| 					ku."table_catalog" = ?',
 | |
| 			'reflect_belongs_to'=>'select
 | |
| 					cu1."table_name",cu1."column_name",
 | |
| 					cu2."table_name",cu2."column_name"
 | |
| 				from
 | |
| 					"information_schema".referential_constraints rc,
 | |
| 					"information_schema".key_column_usage cu1,
 | |
| 					"information_schema".key_column_usage cu2
 | |
| 				where
 | |
| 					cu1."constraint_name" = rc."constraint_name" and
 | |
| 					cu2."constraint_name" = rc."unique_constraint_name" and
 | |
| 					cu1."table_name" = ? and
 | |
| 					cu2."table_name" in ? and
 | |
| 					cu1."table_catalog" = ? and
 | |
| 					cu2."table_catalog" = ?',
 | |
| 			'reflect_has_many'=>'select
 | |
| 					cu1."table_name",cu1."column_name",
 | |
| 					cu2."table_name",cu2."column_name"
 | |
| 				from
 | |
| 					"information_schema".referential_constraints rc,
 | |
| 					"information_schema".key_column_usage cu1,
 | |
| 					"information_schema".key_column_usage cu2
 | |
| 				where
 | |
| 					cu1."constraint_name" = rc."constraint_name" and
 | |
| 					cu2."constraint_name" = rc."unique_constraint_name" and
 | |
| 					cu1."table_name" in ? and
 | |
| 					cu2."table_name" = ? and
 | |
| 					cu1."table_catalog" = ? and
 | |
| 					cu2."table_catalog" = ?',
 | |
| 			'reflect_habtm'=>'select
 | |
| 					cua1."table_name",cua1."column_name",
 | |
| 					cua2."table_name",cua2."column_name",
 | |
| 					cub1."table_name",cub1."column_name",
 | |
| 					cub2."table_name",cub2."column_name"
 | |
| 				from
 | |
| 					"information_schema".referential_constraints rca,
 | |
| 					"information_schema".referential_constraints rcb,
 | |
| 					"information_schema".key_column_usage cua1,
 | |
| 					"information_schema".key_column_usage cua2,
 | |
| 					"information_schema".key_column_usage cub1,
 | |
| 					"information_schema".key_column_usage cub2
 | |
| 				where
 | |
| 					cua1."constraint_name" = rca."constraint_name" and
 | |
| 					cua2."constraint_name" = rca."unique_constraint_name" and
 | |
| 					cub1."constraint_name" = rcb."constraint_name" and
 | |
| 					cub2."constraint_name" = rcb."unique_constraint_name" and
 | |
| 					cua1."table_catalog" = ? and
 | |
| 					cub1."table_catalog" = ? and
 | |
| 					cua2."table_catalog" = ? and
 | |
| 					cub2."table_catalog" = ? and
 | |
| 					cua1."table_name" = cub1."table_name" and
 | |
| 					cua2."table_name" = ? and
 | |
| 					cub2."table_name" in ?'
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	public function getSql($name) {
 | |
| 		return isset($this->queries[$name])?$this->queries[$name]:false;
 | |
| 	}
 | |
| 
 | |
| 	public function connect($hostname,$username,$password,$database,$port,$socket,$charset) {
 | |
| 		$e = function ($v) { return str_replace(array('\'','\\'),array('\\\'','\\\\'),$v); };
 | |
| 		$conn_string = '';
 | |
| 		if ($hostname || $socket) {
 | |
| 			if ($socket) $hostname = $e($socket);
 | |
| 			else $hostname = $e($hostname);
 | |
| 			$conn_string.= " host='$hostname'";
 | |
| 		}
 | |
| 		if ($port) {
 | |
| 			$port = ($port+0);
 | |
| 			$conn_string.= " port='$port'";
 | |
| 		}
 | |
| 		if ($database) {
 | |
| 			$database = $e($database);
 | |
| 			$conn_string.= " dbname='$database'";
 | |
| 		}
 | |
| 		if ($username) {
 | |
| 			$username = $e($username);
 | |
| 			$conn_string.= " user='$username'";
 | |
| 		}
 | |
| 		if ($password) {
 | |
| 			$password = $e($password);
 | |
| 			$conn_string.= " password='$password'";
 | |
| 		}
 | |
| 		if ($charset) {
 | |
| 			$charset = $e($charset);
 | |
| 			$conn_string.= " options='--client_encoding=$charset'";
 | |
| 		}
 | |
| 		$db = pg_connect($conn_string);
 | |
| 		$this->db = $db;
 | |
| 	}
 | |
| 
 | |
| 	public function query($sql,$params) {
 | |
| 		$db = $this->db;
 | |
| 		$sql = preg_replace_callback('/\!|\?/', function ($matches) use (&$db,&$params) {
 | |
| 			$param = array_shift($params);
 | |
| 			if ($matches[0]=='!') {
 | |
| 				$key = preg_replace('/[^a-zA-Z0-9\-_=<> ]/','',is_object($param)?$param->key:$param);
 | |
| 				if (is_object($param) && $param->type=='base64') {
 | |
| 					return "encode(\"$key\",'base64') as \"$key\"";
 | |
| 				}
 | |
| 				if (is_object($param) && $param->type=='wkt') {
 | |
| 					return "ST_AsText(\"$key\") as \"$key\"";
 | |
| 				}
 | |
| 				return '"'.$key.'"';
 | |
| 			} else {
 | |
| 				if (is_array($param)) return '('.implode(',',array_map(function($v) use (&$db) {
 | |
| 					return "'".pg_escape_string($db,$v)."'";
 | |
| 				},$param)).')';
 | |
| 				if (is_object($param) && $param->type=='base64') {
 | |
| 					return "'\x".bin2hex(base64_decode($param->value))."'";
 | |
| 				}
 | |
| 				if (is_object($param) && $param->type=='wkt') {
 | |
| 					return "ST_GeomFromText('".pg_escape_string($db,$param->value)."')";
 | |
| 				}
 | |
| 				if ($param===null) return 'NULL';
 | |
| 				return "'".pg_escape_string($db,$param)."'";
 | |
| 			}
 | |
| 		}, $sql);
 | |
| 		if (strtoupper(substr($sql,0,6))=='INSERT') {
 | |
| 			$sql .= ' RETURNING id;';
 | |
| 		}
 | |
| 		//echo "\n$sql\n";
 | |
| 		return @pg_query($db,$sql);
 | |
| 	}
 | |
| 
 | |
| 	public function fetchAssoc($result) {
 | |
| 		return pg_fetch_assoc($result);
 | |
| 	}
 | |
| 
 | |
| 	public function fetchRow($result) {
 | |
| 		return pg_fetch_row($result);
 | |
| 	}
 | |
| 
 | |
| 	public function insertId($result) {
 | |
| 		list($id) = pg_fetch_row($result);
 | |
| 		return (int)$id;
 | |
| 	}
 | |
| 
 | |
| 	public function affectedRows($result) {
 | |
| 		return pg_affected_rows($result);
 | |
| 	}
 | |
| 
 | |
| 	public function close($result) {
 | |
| 		return pg_free_result($result);
 | |
| 	}
 | |
| 
 | |
| 	public function fetchFields($table) {
 | |
| 		$result = $this->query('SELECT * FROM ! WHERE 1=2;',array($table));
 | |
| 		$keys = array();
 | |
| 		for($i=0;$i<pg_num_fields($result);$i++) {
 | |
| 			$field = array();
 | |
| 			$field['name'] = pg_field_name($result,$i);
 | |
| 			$field['type'] = pg_field_type($result,$i);
 | |
| 			$keys[$i] = (object)$field;
 | |
| 		}
 | |
| 		return $keys;
 | |
| 	}
 | |
| 
 | |
| 	public function addLimitToSql($sql,$limit,$offset) {
 | |
| 		return "$sql LIMIT $limit OFFSET $offset";
 | |
| 	}
 | |
| 
 | |
| 	public function likeEscape($string) {
 | |
| 		return addcslashes($string,'%_');
 | |
| 	}
 | |
| 
 | |
| 	public function convertFilter($field, $comparator, $value) {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	public function isBinaryType($field) {
 | |
| 		return $field->type == 'bytea';
 | |
| 	}
 | |
| 
 | |
| 	public function isGeometryType($field) {
 | |
| 		return $field->type == 'geometry';
 | |
| 	}
 | |
| 
 | |
| 	public function getDefaultCharset() {
 | |
| 		return 'UTF8';
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| class SQLServer implements DatabaseInterface {
 | |
| 
 | |
| 	protected $db;
 | |
| 	protected $queries;
 | |
| 
 | |
| 	public function __construct() {
 | |
| 		$this->queries = array(
 | |
| 			'list_tables'=>'SELECT
 | |
| 					"TABLE_NAME",\'\' as "TABLE_COMMENT"
 | |
| 				FROM
 | |
| 					"INFORMATION_SCHEMA"."TABLES"
 | |
| 				WHERE
 | |
| 					"TABLE_CATALOG" = ?',
 | |
| 			'reflect_table'=>'SELECT
 | |
| 					"TABLE_NAME"
 | |
| 				FROM
 | |
| 					"INFORMATION_SCHEMA"."TABLES"
 | |
| 				WHERE
 | |
| 					"TABLE_NAME" LIKE ? AND
 | |
| 					"TABLE_CATALOG" = ?',
 | |
| 			'reflect_pk'=>'SELECT
 | |
| 					"COLUMN_NAME"
 | |
| 				FROM
 | |
| 					"INFORMATION_SCHEMA"."TABLE_CONSTRAINTS" tc,
 | |
| 					"INFORMATION_SCHEMA"."KEY_COLUMN_USAGE" ku
 | |
| 				WHERE
 | |
| 					tc."CONSTRAINT_TYPE" = \'PRIMARY KEY\' AND
 | |
| 					tc."CONSTRAINT_NAME" = ku."CONSTRAINT_NAME" AND
 | |
| 					ku."TABLE_NAME" = ? AND
 | |
| 					ku."TABLE_CATALOG" = ?',
 | |
| 			'reflect_belongs_to'=>'SELECT
 | |
| 					cu1."TABLE_NAME",cu1."COLUMN_NAME",
 | |
| 					cu2."TABLE_NAME",cu2."COLUMN_NAME"
 | |
| 				FROM
 | |
| 					"INFORMATION_SCHEMA".REFERENTIAL_CONSTRAINTS rc,
 | |
| 					"INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cu1,
 | |
| 					"INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cu2
 | |
| 				WHERE
 | |
| 					cu1."CONSTRAINT_NAME" = rc."CONSTRAINT_NAME" AND
 | |
| 					cu2."CONSTRAINT_NAME" = rc."UNIQUE_CONSTRAINT_NAME" AND
 | |
| 					cu1."TABLE_NAME" = ? AND
 | |
| 					cu2."TABLE_NAME" IN ? AND
 | |
| 					cu1."TABLE_CATALOG" = ? AND
 | |
| 					cu2."TABLE_CATALOG" = ?',
 | |
| 			'reflect_has_many'=>'SELECT
 | |
| 					cu1."TABLE_NAME",cu1."COLUMN_NAME",
 | |
| 					cu2."TABLE_NAME",cu2."COLUMN_NAME"
 | |
| 				FROM
 | |
| 					"INFORMATION_SCHEMA".REFERENTIAL_CONSTRAINTS rc,
 | |
| 					"INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cu1,
 | |
| 					"INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cu2
 | |
| 				WHERE
 | |
| 					cu1."CONSTRAINT_NAME" = rc."CONSTRAINT_NAME" AND
 | |
| 					cu2."CONSTRAINT_NAME" = rc."UNIQUE_CONSTRAINT_NAME" AND
 | |
| 					cu1."TABLE_NAME" IN ? AND
 | |
| 					cu2."TABLE_NAME" = ? AND
 | |
| 					cu1."TABLE_CATALOG" = ? AND
 | |
| 					cu2."TABLE_CATALOG" = ?',
 | |
| 			'reflect_habtm'=>'SELECT
 | |
| 					cua1."TABLE_NAME",cua1."COLUMN_NAME",
 | |
| 					cua2."TABLE_NAME",cua2."COLUMN_NAME",
 | |
| 					cub1."TABLE_NAME",cub1."COLUMN_NAME",
 | |
| 					cub2."TABLE_NAME",cub2."COLUMN_NAME"
 | |
| 				FROM
 | |
| 					"INFORMATION_SCHEMA".REFERENTIAL_CONSTRAINTS rca,
 | |
| 					"INFORMATION_SCHEMA".REFERENTIAL_CONSTRAINTS rcb,
 | |
| 					"INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cua1,
 | |
| 					"INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cua2,
 | |
| 					"INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cub1,
 | |
| 					"INFORMATION_SCHEMA".CONSTRAINT_COLUMN_USAGE cub2
 | |
| 				WHERE
 | |
| 					cua1."CONSTRAINT_NAME" = rca."CONSTRAINT_NAME" AND
 | |
| 					cua2."CONSTRAINT_NAME" = rca."UNIQUE_CONSTRAINT_NAME" AND
 | |
| 					cub1."CONSTRAINT_NAME" = rcb."CONSTRAINT_NAME" AND
 | |
| 					cub2."CONSTRAINT_NAME" = rcb."UNIQUE_CONSTRAINT_NAME" AND
 | |
| 					cua1."TABLE_CATALOG" = ? AND
 | |
| 					cub1."TABLE_CATALOG" = ? AND
 | |
| 					cua2."TABLE_CATALOG" = ? AND
 | |
| 					cub2."TABLE_CATALOG" = ? AND
 | |
| 					cua1."TABLE_NAME" = cub1."TABLE_NAME" AND
 | |
| 					cua2."TABLE_NAME" = ? AND
 | |
| 					cub2."TABLE_NAME" IN ?'
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	public function getSql($name) {
 | |
| 		return isset($this->queries[$name])?$this->queries[$name]:false;
 | |
| 	}
 | |
| 
 | |
| 	public function connect($hostname,$username,$password,$database,$port,$socket,$charset) {
 | |
| 		$connectionInfo = array();
 | |
| 		if ($port) $hostname.=','.$port;
 | |
| 		if ($username) $connectionInfo['UID']=$username;
 | |
| 		if ($password) $connectionInfo['PWD']=$password;
 | |
| 		if ($database) $connectionInfo['Database']=$database;
 | |
| 		if ($charset) $connectionInfo['CharacterSet']=$charset;
 | |
| 		$connectionInfo['QuotedId']=1;
 | |
| 		$connectionInfo['ReturnDatesAsStrings']=1;
 | |
| 
 | |
| 		$db = sqlsrv_connect($hostname, $connectionInfo);
 | |
| 		if (!$db) {
 | |
| 			throw new \Exception('Connect failed. '.print_r( sqlsrv_errors(), true));
 | |
| 		}
 | |
| 		if ($socket) {
 | |
| 			throw new \Exception('Socket connection is not supported.');
 | |
| 		}
 | |
| 		$this->db = $db;
 | |
| 	}
 | |
| 
 | |
| 	public function query($sql,$params) {
 | |
| 		$args = array();
 | |
| 		$db = $this->db;
 | |
| 		$sql = preg_replace_callback('/\!|\?/', function ($matches) use (&$db,&$params,&$args) {
 | |
| 			static $i=-1;
 | |
| 			$i++;
 | |
| 			$param = $params[$i];
 | |
| 			if ($matches[0]=='!') {
 | |
| 				$key = preg_replace('/[^a-zA-Z0-9\-_=<> ]/','',is_object($param)?$param->key:$param);
 | |
| 				if (is_object($param) && $param->type=='base64') {
 | |
| 					return "CAST(N'' AS XML).value('xs:base64Binary(xs:hexBinary(sql:column(\"$key\")))', 'VARCHAR(MAX)') as \"$key\"";
 | |
| 				}
 | |
| 				if (is_object($param) && $param->type=='wkt') {
 | |
| 					return "\"$key\".STAsText() as \"$key\"";
 | |
| 				}
 | |
| 				return '"'.$key.'"';
 | |
| 			} else {
 | |
| 				// This is workaround because SQLSRV cannot accept NULL in a param
 | |
| 				if ($matches[0]=='?' && is_null($param)) {
 | |
| 					return 'NULL';
 | |
| 				}
 | |
| 				if (is_array($param)) {
 | |
| 					$args = array_merge($args,$param);
 | |
| 					return '('.implode(',',str_split(str_repeat('?',count($param)))).')';
 | |
| 				}
 | |
| 				if (is_object($param) && $param->type=='base64') {
 | |
| 					$args[] = bin2hex(base64_decode($param->value));
 | |
| 					return 'CONVERT(VARBINARY(MAX),?,2)';
 | |
| 				}
 | |
| 				if (is_object($param) && $param->type=='wkt') {
 | |
| 					$args[] = $param->value;
 | |
| 					return 'geometry::STGeomFromText(?,0)';
 | |
| 				}
 | |
| 				$args[] = $param;
 | |
| 				return '?';
 | |
| 			}
 | |
| 		}, $sql);
 | |
| 		//var_dump($params);
 | |
| 		//echo "\n$sql\n";
 | |
| 		//var_dump($args);
 | |
| 		//file_put_contents('sql.txt',"\n$sql\n".var_export($args,true)."\n",FILE_APPEND);
 | |
| 		if (strtoupper(substr($sql,0,6))=='INSERT') {
 | |
| 			$sql .= ';SELECT SCOPE_IDENTITY()';
 | |
| 		}
 | |
| 		return sqlsrv_query($db,$sql,$args)?:null;
 | |
| 	}
 | |
| 
 | |
| 	public function fetchAssoc($result) {
 | |
| 		$values = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC);
 | |
| 		if ($values) $values = array_map(function($v){ return is_null($v)?null:(string)$v; },$values);
 | |
| 		return $values;
 | |
| 	}
 | |
| 
 | |
| 	public function fetchRow($result) {
 | |
| 		$values = sqlsrv_fetch_array($result, SQLSRV_FETCH_NUMERIC);
 | |
| 		if ($values) $values = array_map(function($v){ return is_null($v)?null:(string)$v; },$values);
 | |
| 		return $values;
 | |
| 	}
 | |
| 
 | |
| 	public function insertId($result) {
 | |
| 		sqlsrv_next_result($result);
 | |
| 		sqlsrv_fetch($result);
 | |
| 		return (int)sqlsrv_get_field($result, 0);
 | |
| 	}
 | |
| 
 | |
| 	public function affectedRows($result) {
 | |
| 		return sqlsrv_rows_affected($result);
 | |
| 	}
 | |
| 
 | |
| 	public function close($result) {
 | |
| 		return sqlsrv_free_stmt($result);
 | |
| 	}
 | |
| 
 | |
| 	public function fetchFields($table) {
 | |
| 		$result = $this->query('SELECT * FROM ! WHERE 1=2;',array($table));
 | |
| 		//var_dump(sqlsrv_field_metadata($result));
 | |
| 		return array_map(function($a){
 | |
| 			$p = array();
 | |
| 			foreach ($a as $k=>$v) {
 | |
| 				$p[strtolower($k)] = $v;
 | |
| 			}
 | |
| 			return (object)$p;
 | |
| 		},sqlsrv_field_metadata($result));
 | |
| 	}
 | |
| 
 | |
| 	public function addLimitToSql($sql,$limit,$offset) {
 | |
| 		return "$sql OFFSET $offset ROWS FETCH NEXT $limit ROWS ONLY";
 | |
| 	}
 | |
| 
 | |
| 	public function likeEscape($string) {
 | |
| 		return str_replace(array('%','_'),array('[%]','[_]'),$string);
 | |
| 	}
 | |
| 
 | |
| 	public function convertFilter($field, $comparator, $value) {
 | |
| 		$comparator = strtolower($comparator);
 | |
| 		if ($comparator[0]!='n') {
 | |
| 			switch ($comparator) {
 | |
| 				case 'sco': return array('!.STContains(geometry::STGeomFromText(?,0))=1',$field,$value);
 | |
| 				case 'scr': return array('!.STCrosses(geometry::STGeomFromText(?,0))=1',$field,$value);
 | |
| 				case 'sdi': return array('!.STDisjoint(geometry::STGeomFromText(?,0))=1',$field,$value);
 | |
| 				case 'seq': return array('!.STEquals(geometry::STGeomFromText(?,0))=1',$field,$value);
 | |
| 				case 'sin': return array('!.STIntersects(geometry::STGeomFromText(?,0))=1',$field,$value);
 | |
| 				case 'sov': return array('!.STOverlaps(geometry::STGeomFromText(?,0))=1',$field,$value);
 | |
| 				case 'sto': return array('!.STTouches(geometry::STGeomFromText(?,0))=1',$field,$value);
 | |
| 				case 'swi': return array('!.STWithin(geometry::STGeomFromText(?,0))=1',$field,$value);
 | |
| 				case 'sic': return array('!.STIsClosed()=1',$field);
 | |
| 				case 'sis': return array('!.STIsSimple()=1',$field);
 | |
| 				case 'siv': return array('!.STIsValid()=1',$field);
 | |
| 			}
 | |
| 		} else {
 | |
| 			switch ($comparator) {
 | |
| 				case 'nsco': return array('!.STContains(geometry::STGeomFromText(?,0))=0',$field,$value);
 | |
| 				case 'nscr': return array('!.STCrosses(geometry::STGeomFromText(?,0))=0',$field,$value);
 | |
| 				case 'nsdi': return array('!.STDisjoint(geometry::STGeomFromText(?,0))=0',$field,$value);
 | |
| 				case 'nseq': return array('!.STEquals(geometry::STGeomFromText(?,0))=0',$field,$value);
 | |
| 				case 'nsin': return array('!.STIntersects(geometry::STGeomFromText(?,0))=0',$field,$value);
 | |
| 				case 'nsov': return array('!.STOverlaps(geometry::STGeomFromText(?,0))=0',$field,$value);
 | |
| 				case 'nsto': return array('!.STTouches(geometry::STGeomFromText(?,0))=0',$field,$value);
 | |
| 				case 'nswi': return array('!.STWithin(geometry::STGeomFromText(?,0))=0',$field,$value);
 | |
| 				case 'nsic': return array('!.STIsClosed()=0',$field);
 | |
| 				case 'nsis': return array('!.STIsSimple()=0',$field);
 | |
| 				case 'nsiv': return array('!.STIsValid()=0',$field);
 | |
| 			}
 | |
| 		} 
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	public function isBinaryType($field) {
 | |
| 		return ($field->type>=-4 && $field->type<=-2);
 | |
| 	}
 | |
| 
 | |
| 	public function isGeometryType($field) {
 | |
| 		return ($field->type==-151);
 | |
| 	}
 | |
| 
 | |
| 	public function getDefaultCharset() {
 | |
| 		return 'UTF-8';
 | |
| 	}
 | |
| }
 | |
| 
 | |
| class SQLite implements DatabaseInterface {
 | |
| 
 | |
| 	protected $db;
 | |
| 	protected $queries;
 | |
| 
 | |
| 	public function __construct() {
 | |
| 		$this->queries = array(
 | |
| 			'list_tables'=>'SELECT
 | |
| 					"name", ""
 | |
| 				FROM
 | |
| 					"sys/tables"',
 | |
| 			'reflect_table'=>'SELECT
 | |
| 					"name"
 | |
| 				FROM
 | |
| 					"sys/tables"
 | |
| 				WHERE
 | |
| 					"name"=?',
 | |
| 			'reflect_pk'=>'SELECT
 | |
| 					"name"
 | |
| 				FROM
 | |
| 					"sys/columns"
 | |
| 				WHERE
 | |
| 					"pk"=1 AND
 | |
| 					"self"=?',
 | |
| 			'reflect_belongs_to'=>'SELECT
 | |
| 					"self", "from",
 | |
| 					"table", "to"
 | |
| 				FROM
 | |
| 					"sys/foreign_keys"
 | |
| 				WHERE
 | |
| 					"self" = ? AND
 | |
| 					"table" IN ? AND
 | |
| 					? like "%" AND
 | |
| 					? like "%"',
 | |
| 			'reflect_has_many'=>'SELECT
 | |
| 					"self", "from",
 | |
| 					"table", "to"
 | |
| 				FROM
 | |
| 					"sys/foreign_keys"
 | |
| 				WHERE
 | |
| 					"self" IN ? AND
 | |
| 					"table" = ? AND
 | |
| 					? like "%" AND
 | |
| 					? like "%"',
 | |
| 			'reflect_habtm'=>'SELECT
 | |
| 					k1."self", k1."from",
 | |
| 					k1."table", k1."to",
 | |
| 					k2."self", k2."from",
 | |
| 					k2."table", k2."to"
 | |
| 				FROM
 | |
| 					"sys/foreign_keys" k1,
 | |
| 					"sys/foreign_keys" k2
 | |
| 				WHERE
 | |
| 					? like "%" AND
 | |
| 					? like "%" AND
 | |
| 					? like "%" AND
 | |
| 					? like "%" AND
 | |
| 					k1."self" = k2."self" AND
 | |
| 					k1."table" = ? AND
 | |
| 					k2."table" IN ?'
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	public function getSql($name) {
 | |
| 		return isset($this->queries[$name])?$this->queries[$name]:false;
 | |
| 	}
 | |
| 
 | |
| 	public function connect($hostname,$username,$password,$database,$port,$socket,$charset) {
 | |
| 		$this->db = new SQLite3($database);
 | |
| 		// optimizations
 | |
| 		$this->db->querySingle('PRAGMA synchronous = NORMAL');
 | |
| 		$this->db->querySingle('PRAGMA foreign_keys = on');
 | |
| 		$reflection = $this->db->querySingle('SELECT name FROM sqlite_master WHERE type = "table" and name like "sys/%"');
 | |
| 		if (!$reflection) {
 | |
| 			//create reflection tables
 | |
| 			$this->query('CREATE table "sys/version" ("version" integer)');
 | |
| 			$this->query('CREATE table "sys/tables" ("name" text)');
 | |
| 			$this->query('CREATE table "sys/columns" ("self" text,"cid" integer,"name" text,"type" integer,"notnull" integer,"dflt_value" integer,"pk" integer)');
 | |
| 			$this->query('CREATE table "sys/foreign_keys" ("self" text,"id" integer,"seq" integer,"table" text,"from" text,"to" text,"on_update" text,"on_delete" text,"match" text)');
 | |
| 		}
 | |
| 		$version = $this->db->querySingle('pragma schema_version');
 | |
| 		if ($version != $this->db->querySingle('SELECT "version" from "sys/version"')) {
 | |
| 			// reflection may take a while
 | |
| 			set_time_limit(3600);
 | |
| 			// update version data
 | |
| 			$this->query('DELETE FROM "sys/version"');
 | |
| 			$this->query('INSERT into "sys/version" ("version") VALUES (?)',array($version));
 | |
| 			// update tables data
 | |
| 			$this->query('DELETE FROM "sys/tables"');
 | |
| 			$result = $this->query('SELECT * FROM sqlite_master WHERE type = "table" and name not like "sys/%" and name<>"sqlite_sequence"');
 | |
| 			$tables = array();
 | |
| 			while ($row = $this->fetchAssoc($result)) {
 | |
| 				$tables[] = $row['name'];
 | |
| 				$this->query('INSERT into "sys/tables" ("name") VALUES (?)',array($row['name']));
 | |
| 			}
 | |
| 			// update columns and foreign_keys data
 | |
| 			$this->query('DELETE FROM "sys/columns"');
 | |
| 			$this->query('DELETE FROM "sys/foreign_keys"');
 | |
| 			foreach ($tables as $table) {
 | |
| 				$result = $this->query('pragma table_info(!)',array($table));
 | |
| 				while ($row = $this->fetchRow($result)) {
 | |
| 					array_unshift($row, $table);
 | |
| 					$this->query('INSERT into "sys/columns" ("self","cid","name","type","notnull","dflt_value","pk") VALUES (?,?,?,?,?,?,?)',$row);
 | |
| 				}
 | |
| 				$result = $this->query('pragma foreign_key_list(!)',array($table));
 | |
| 				while ($row = $this->fetchRow($result)) {
 | |
| 					array_unshift($row, $table);
 | |
| 					$this->query('INSERT into "sys/foreign_keys" ("self","id","seq","table","from","to","on_update","on_delete","match") VALUES (?,?,?,?,?,?,?,?,?)',$row);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public function query($sql,$params=array()) {
 | |
| 		$db = $this->db;
 | |
| 		$sql = preg_replace_callback('/\!|\?/', function ($matches) use (&$db,&$params) {
 | |
| 			$param = array_shift($params);
 | |
| 			if ($matches[0]=='!') {
 | |
| 				$key = preg_replace('/[^a-zA-Z0-9\-_=<> ]/','',is_object($param)?$param->key:$param);
 | |
| 				return '"'.$key.'"';
 | |
| 			} else {
 | |
| 				if (is_array($param)) return '('.implode(',',array_map(function($v) use (&$db) {
 | |
| 					return "'".$db->escapeString($v)."'";
 | |
| 				},$param)).')';
 | |
| 				if (is_object($param) && $param->type=='base64') {
 | |
| 					return "'".$db->escapeString($param->value)."'";
 | |
| 				}
 | |
| 				if ($param===null) return 'NULL';
 | |
| 				return "'".$db->escapeString($param)."'";
 | |
| 			}
 | |
| 		}, $sql);
 | |
| 		//echo "\n$sql\n";
 | |
| 		try {	$result=$db->query($sql); } catch(\Exception $e) { $result=null; }
 | |
| 		return $result;
 | |
| 	}
 | |
| 
 | |
| 	public function fetchAssoc($result) {
 | |
| 		$values = $result->fetchArray(SQLITE3_ASSOC);
 | |
| 		if ($values) $values = array_map(function($v){ return is_null($v)?null:(string)$v; },$values);
 | |
| 		return $values;
 | |
| 	}
 | |
| 
 | |
| 	public function fetchRow($result) {
 | |
| 		$values = $result->fetchArray(SQLITE3_NUM);
 | |
| 		if ($values) $values = array_map(function($v){ return is_null($v)?null:(string)$v; },$values);
 | |
| 		return $values;
 | |
| 	}
 | |
| 
 | |
| 	public function insertId($result) {
 | |
| 		return $this->db->lastInsertRowID();
 | |
| 	}
 | |
| 
 | |
| 	public function affectedRows($result) {
 | |
| 		return $this->db->changes();
 | |
| 	}
 | |
| 
 | |
| 	public function close($result) {
 | |
| 		return $result->finalize();
 | |
| 	}
 | |
| 
 | |
| 	public function fetchFields($table) {
 | |
| 		$result = $this->query('SELECT * FROM "sys/columns" WHERE "self"=?;',array($table));
 | |
| 		$fields = array();
 | |
| 		while ($row = $this->fetchAssoc($result)){
 | |
| 			$fields[strtolower($row['name'])] = (object)$row;
 | |
| 		}
 | |
| 		return $fields;
 | |
| 	}
 | |
| 
 | |
| 	public function addLimitToSql($sql,$limit,$offset) {
 | |
| 		return "$sql LIMIT $limit OFFSET $offset";
 | |
| 	}
 | |
| 
 | |
| 	public function likeEscape($string) {
 | |
| 		return addcslashes($string,'%_');
 | |
| 	}
 | |
| 
 | |
| 	public function convertFilter($field, $comparator, $value) {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	public function isBinaryType($field) {
 | |
| 		return (substr($field->type,0,4)=='data');
 | |
| 	}
 | |
| 
 | |
| 	public function isGeometryType($field) {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	public function getDefaultCharset() {
 | |
| 		return 'utf8';
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| class PHP_CRUD_API {
 | |
| 
 | |
| 	protected $db;
 | |
| 	protected $settings;
 | |
| 
 | |
| 	protected function mapMethodToAction($method,$key) {
 | |
| 		switch ($method) {
 | |
| 			case 'OPTIONS': return 'headers';
 | |
| 			case 'GET': return $key?'read':'list';
 | |
| 			case 'PUT': return 'update';
 | |
| 			case 'POST': return 'create';
 | |
| 			case 'DELETE': return 'delete';
 | |
| 			default: $this->exitWith404('method');
 | |
| 		}
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	protected function parseRequestParameter(&$request,$characters) {
 | |
| 		if (!$request) return false;
 | |
| 		$pos = strpos($request,'/');
 | |
| 		$value = $pos?substr($request,0,$pos):$request;
 | |
| 		$request = $pos?substr($request,$pos+1):'';
 | |
| 		if (!$characters) return $value;
 | |
| 		return preg_replace("/[^$characters]/",'',$value);
 | |
| 	}
 | |
| 
 | |
| 	protected function parseGetParameter($get,$name,$characters) {
 | |
| 		$value = isset($get[$name])?$get[$name]:false;
 | |
| 		return $characters?preg_replace("/[^$characters]/",'',$value):$value;
 | |
| 	}
 | |
| 
 | |
| 	protected function parseGetParameterArray($get,$name,$characters) {
 | |
| 		$values = isset($get[$name])?$get[$name]:false;
 | |
| 		if (!is_array($values)) $values = array($values);
 | |
| 		if ($characters) {
 | |
| 			foreach ($values as &$value) {
 | |
| 				$value = preg_replace("/[^$characters]/",'',$value);
 | |
| 			}
 | |
| 		}
 | |
| 		return $values;
 | |
| 	}
 | |
| 
 | |
| 	protected function applyTableAuthorizer($callback,$action,$database,&$tables) {
 | |
| 		if (is_callable($callback,true)) foreach ($tables as $i=>$table) {
 | |
| 			if (!$callback($action,$database,$table)) {
 | |
| 				unset($tables[$i]);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function applyRecordFilter($callback,$action,$database,$tables,&$filters) {
 | |
| 		if (is_callable($callback,true)) foreach ($tables as $i=>$table) {
 | |
| 			$f = $this->convertFilters($callback($action,$database,$table));
 | |
| 			if ($f) {
 | |
| 				if (!isset($filters[$table])) $filters[$table] = array();
 | |
| 				if (!isset($filters[$table]['and'])) $filters[$table]['and'] = array();
 | |
| 				$filters[$table]['and'] = array_merge($filters[$table]['and'],$f);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function applyTenancyFunction($callback,$action,$database,$fields,&$filters) {
 | |
| 		if (is_callable($callback,true)) foreach ($fields as $table=>$keys) {
 | |
| 			foreach ($keys as $field) {
 | |
| 				$v = $callback($action,$database,$table,$field->name);
 | |
| 				if ($v!==null) {
 | |
| 					if (!isset($filters[$table])) $filters[$table] = array();
 | |
| 					if (!isset($filters[$table]['and'])) $filters[$table]['and'] = array();
 | |
| 					if (is_array($v)) $filters[$table]['and'][] = $this->convertFilter($field->name,'in',implode(',',$v));
 | |
| 					else $filters[$table]['and'][] = $this->convertFilter($field->name,'eq',$v);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function applyColumnAuthorizer($callback,$action,$database,&$fields) {
 | |
| 		if (is_callable($callback,true)) foreach ($fields as $table=>$keys) {
 | |
| 			foreach ($keys as $field) {
 | |
| 				if (!$callback($action,$database,$table,$field->name)) {
 | |
| 					unset($fields[$table][$field->name]);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function applyInputTenancy($callback,$action,$database,$table,&$input,$keys) {
 | |
| 		if (is_callable($callback,true)) foreach ($keys as $key=>$field) {
 | |
| 			$v = $callback($action,$database,$table,$key);
 | |
| 			if ($v!==null && (isset($input->$key) || $action=='create')) {
 | |
| 				if (is_array($v)) {
 | |
| 					if (!count($v)) {
 | |
| 						$input->$key = null;
 | |
| 					} elseif (!isset($input->$key)) {
 | |
| 						$input->$key = $v[0];
 | |
| 					} elseif (!in_array($input->$key,$v)) {
 | |
| 						$input->$key = null;
 | |
| 					}
 | |
| 				} else {
 | |
| 					$input->$key = $v;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function applyInputSanitizer($callback,$action,$database,$table,&$input,$keys) {
 | |
| 		if (is_callable($callback,true)) foreach ((array)$input as $key=>$value) {
 | |
| 			if (isset($keys[$key])) {
 | |
| 				$input->$key = $callback($action,$database,$table,$key,$keys[$key]->type,$value);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function applyInputValidator($callback,$action,$database,$table,$input,$keys,$context) {
 | |
| 		$errors = array();
 | |
| 		if (is_callable($callback,true)) foreach ((array)$input as $key=>$value) {
 | |
| 			if (isset($keys[$key])) {
 | |
| 				$error = $callback($action,$database,$table,$key,$keys[$key]->type,$value,$context);
 | |
| 				if ($error!==true && $error!==null) $errors[$key] = $error;
 | |
| 			}
 | |
| 		}
 | |
| 		if (!empty($errors)) $this->exitWith422($errors);
 | |
| 	}
 | |
| 
 | |
| 	protected function processTableAndIncludeParameters($database,$table,$include,$action) {
 | |
| 		$blacklist = array('information_schema','mysql','sys','pg_catalog');
 | |
| 		if (in_array(strtolower($database), $blacklist)) return array();
 | |
| 		$table_list = array();
 | |
| 		if ($result = $this->db->query($this->db->getSql('reflect_table'),array($table,$database))) {
 | |
| 			while ($row = $this->db->fetchRow($result)) $table_list[] = $row[0];
 | |
| 			$this->db->close($result);
 | |
| 		}
 | |
| 		if (empty($table_list)) $this->exitWith404('entity');
 | |
| 		if ($action=='list') {
 | |
| 			foreach (explode(',',$include) as $table) {
 | |
| 				if ($result = $this->db->query($this->db->getSql('reflect_table'),array($table,$database))) {
 | |
| 					while ($row = $this->db->fetchRow($result)) $table_list[] = $row[0];
 | |
| 					$this->db->close($result);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return $table_list;
 | |
| 	}
 | |
| 
 | |
| 	protected function exitWith404($type) {
 | |
| 		if (isset($_SERVER['REQUEST_METHOD'])) {
 | |
| 			header('Content-Type:',true,404);
 | |
| 			die("Not found ($type)");
 | |
| 		} else {
 | |
| 			throw new \Exception("Not found ($type)");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function exitWith422($object) {
 | |
| 		if (isset($_SERVER['REQUEST_METHOD'])) {
 | |
| 			header('Content-Type:',true,422);
 | |
| 			die(json_encode($object));
 | |
| 		} else {
 | |
| 			throw new \Exception(json_encode($object));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function headersCommand($parameters) {
 | |
| 		$headers = array();
 | |
| 		$headers[]='Access-Control-Allow-Headers: Content-Type';
 | |
| 		$headers[]='Access-Control-Allow-Methods: OPTIONS, GET, PUT, POST, DELETE';
 | |
| 		$headers[]='Access-Control-Allow-Credentials: true';
 | |
| 		$headers[]='Access-Control-Max-Age: 1728000';
 | |
| 		if (isset($_SERVER['REQUEST_METHOD'])) {
 | |
| 			foreach ($headers as $header) header($header);
 | |
| 		} else {
 | |
| 			echo json_encode($headers);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function startOutput($callback) {
 | |
| 		if ($callback) {
 | |
| 			if (isset($_SERVER['REQUEST_METHOD'])) {
 | |
| 				header('Content-Type: application/javascript; charset=utf-8');
 | |
| 			}
 | |
| 			echo $callback.'(';
 | |
| 		} else {
 | |
| 			if (isset($_SERVER['REQUEST_METHOD'])) {
 | |
| 				header('Content-Type: application/json; charset=utf-8');
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function endOutput($callback) {
 | |
| 		if ($callback) {
 | |
| 			echo ');';
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function findPrimaryKeys($table,$database) {
 | |
| 		$fields = array();
 | |
| 		if ($result = $this->db->query($this->db->getSql('reflect_pk'),array($table,$database))) {
 | |
| 			while ($row = $this->db->fetchRow($result)) {
 | |
| 				$fields[] = $row[0];
 | |
| 			}
 | |
| 			$this->db->close($result);
 | |
| 		}
 | |
| 		return $fields;
 | |
| 	}
 | |
| 
 | |
| 	protected function processKeyParameter($key,$tables,$database) {
 | |
| 		if (!$key) return false;
 | |
| 		$fields = $this->findPrimaryKeys($tables[0],$database);
 | |
| 		if (count($fields)!=1) $this->exitWith404('1pk');
 | |
| 		return array($key,$fields[0]);
 | |
| 	}
 | |
| 
 | |
| 	protected function processOrderParameter($order) {
 | |
| 		if (!$order) return false;
 | |
| 		$order = explode(',',$order,2);
 | |
| 		if (count($order)<2) $order[1]='ASC';
 | |
| 		if (!strlen($order[0])) return false;
 | |
| 		$order[1] = strtoupper($order[1])=='DESC'?'DESC':'ASC';
 | |
| 		return $order;
 | |
| 	}
 | |
| 
 | |
| 	protected function convertFilter($field, $comparator, $value) {
 | |
| 		$result = $this->db->convertFilter($field,$comparator,$value);
 | |
| 		if ($result) return $result;
 | |
| 		// default behavior 					
 | |
| 		$comparator = strtolower($comparator);
 | |
| 		if ($comparator[0]!='n') {
 | |
| 			if (strlen($comparator)==2) {
 | |
| 				switch ($comparator) {
 | |
| 					case 'cs': return array('! LIKE ?',$field,'%'.$this->db->likeEscape($value).'%');
 | |
| 					case 'sw': return array('! LIKE ?',$field,$this->db->likeEscape($value).'%');
 | |
| 					case 'ew': return array('! LIKE ?',$field,'%'.$this->db->likeEscape($value));
 | |
| 					case 'eq': return array('! = ?',$field,$value);
 | |
| 					case 'lt': return array('! < ?',$field,$value);
 | |
| 					case 'le': return array('! <= ?',$field,$value);
 | |
| 					case 'ge': return array('! >= ?',$field,$value);
 | |
| 					case 'gt': return array('! > ?',$field,$value);
 | |
| 					case 'bt': $v = explode(',',$value); if (count($v)<2) return false;
 | |
| 					           return array('! BETWEEN ? AND ?',$field,$v[0],$v[1]);
 | |
| 					case 'in': return array('! IN ?',$field,explode(',',$value));
 | |
| 					case 'is': return array('! IS NULL',$field);
 | |
| 				}
 | |
| 			} else {
 | |
| 				switch ($comparator) {
 | |
| 					case 'sco': return array('ST_Contains(!,ST_GeomFromText(?))=TRUE',$field,$value);
 | |
| 					case 'scr': return array('ST_Crosses(!,ST_GeomFromText(?))=TRUE',$field,$value);
 | |
| 					case 'sdi': return array('ST_Disjoint(!,ST_GeomFromText(?))=TRUE',$field,$value);
 | |
| 					case 'seq': return array('ST_Equals(!,ST_GeomFromText(?))=TRUE',$field,$value);
 | |
| 					case 'sin': return array('ST_Intersects(!,ST_GeomFromText(?))=TRUE',$field,$value);
 | |
| 					case 'sov': return array('ST_Overlaps(!,ST_GeomFromText(?))=TRUE',$field,$value);
 | |
| 					case 'sto': return array('ST_Touches(!,ST_GeomFromText(?))=TRUE',$field,$value);
 | |
| 					case 'swi': return array('ST_Within(!,ST_GeomFromText(?))=TRUE',$field,$value);
 | |
| 					case 'sic': return array('ST_IsClosed(!)=TRUE',$field);
 | |
| 					case 'sis': return array('ST_IsSimple(!)=TRUE',$field);
 | |
| 					case 'siv': return array('ST_IsValid(!)=TRUE',$field);
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			if (strlen($comparator)==2) {
 | |
| 				switch ($comparator) {
 | |
| 					case 'ne': return $this->convertFilter($field, 'neq', $value); // deprecated
 | |
| 					case 'ni': return $this->convertFilter($field, 'nin', $value); // deprecated
 | |
| 					case 'no': return $this->convertFilter($field, 'nis', $value); // deprecated
 | |
| 				}
 | |
| 			} elseif (strlen($comparator)==3) {
 | |
| 				switch ($comparator) {
 | |
| 					case 'ncs': return array('! NOT LIKE ?',$field,'%'.$this->db->likeEscape($value).'%');
 | |
| 					case 'nsw': return array('! NOT LIKE ?',$field,$this->db->likeEscape($value).'%');
 | |
| 					case 'new': return array('! NOT LIKE ?',$field,'%'.$this->db->likeEscape($value));
 | |
| 					case 'neq': return array('! <> ?',$field,$value);
 | |
| 					case 'nlt': return array('! >= ?',$field,$value);
 | |
| 					case 'nle': return array('! > ?',$field,$value);
 | |
| 					case 'nge': return array('! < ?',$field,$value);
 | |
| 					case 'ngt': return array('! <= ?',$field,$value);
 | |
| 					case 'nbt': $v = explode(',',$value); if (count($v)<2) return false;
 | |
| 					            return array('! NOT BETWEEN ? AND ?',$field,$v[0],$v[1]);
 | |
| 					case 'nin': return array('! NOT IN ?',$field,explode(',',$value));
 | |
| 					case 'nis': return array('! IS NOT NULL',$field);
 | |
| 				}
 | |
| 			} else {
 | |
| 				switch ($comparator) {
 | |
| 					case 'nsco': return array('ST_Contains(!,ST_GeomFromText(?))=FALSE',$field,$value);
 | |
| 					case 'nscr': return array('ST_Crosses(!,ST_GeomFromText(?))=FALSE',$field,$value);
 | |
| 					case 'nsdi': return array('ST_Disjoint(!,ST_GeomFromText(?))=FALSE',$field,$value);
 | |
| 					case 'nseq': return array('ST_Equals(!,ST_GeomFromText(?))=FALSE',$field,$value);
 | |
| 					case 'nsin': return array('ST_Intersects(!,ST_GeomFromText(?))=FALSE',$field,$value);
 | |
| 					case 'nsov': return array('ST_Overlaps(!,ST_GeomFromText(?))=FALSE',$field,$value);
 | |
| 					case 'nsto': return array('ST_Touches(!,ST_GeomFromText(?))=FALSE',$field,$value);
 | |
| 					case 'nswi': return array('ST_Within(!,ST_GeomFromText(?))=FALSE',$field,$value);
 | |
| 					case 'nsic': return array('ST_IsClosed(!)=FALSE',$field);
 | |
| 					case 'nsis': return array('ST_IsSimple(!)=FALSE',$field);
 | |
| 					case 'nsiv': return array('ST_IsValid(!)=FALSE',$field);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	public function convertFilters($filters) {
 | |
| 		$result = array();
 | |
| 		if ($filters) {
 | |
| 			for ($i=0;$i<count($filters);$i++) {
 | |
| 				$parts = explode(',',$filters[$i],3);
 | |
| 				if (count($parts)>=2) {
 | |
| 					$field = $parts[0];
 | |
| 					$comparator = $parts[1];
 | |
| 					$value = isset($parts[2])?$parts[2]:null;
 | |
| 					$filter = $this->convertFilter($field,$comparator,$value);
 | |
| 					if ($filter) $result[] = $filter;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return $result;
 | |
| 	}
 | |
| 
 | |
| 	protected function processFiltersParameter($tables,$satisfy,$filters) {
 | |
| 		$result = $this->convertFilters($filters);
 | |
| 		if (!$result) return array();
 | |
| 		$and = ($satisfy && strtolower($satisfy)=='any')?'or':'and';
 | |
| 		return array($tables[0]=>array($and=>$result));
 | |
| 	}
 | |
| 
 | |
| 	protected function processPageParameter($page) {
 | |
| 		if (!$page) return false;
 | |
| 		$page = explode(',',$page,2);
 | |
| 		if (count($page)<2) $page[1]=20;
 | |
| 		$page[0] = ($page[0]-1)*$page[1];
 | |
| 		return $page;
 | |
| 	}
 | |
| 
 | |
| 	protected function retrieveObject($key,$fields,$filters,$tables) {
 | |
| 		if (!$key) return false;
 | |
| 		$table = $tables[0];
 | |
| 		$params = array();
 | |
| 		$sql = 'SELECT ';
 | |
| 		$this->convertOutputs($sql,$params,$fields[$table]);
 | |
| 		$sql .= ' FROM !';
 | |
| 		$params[] = $table;
 | |
| 		if (!isset($filters[$table])) $filters[$table] = array();
 | |
| 		if (!isset($filters[$table]['or'])) $filters[$table]['or'] = array();
 | |
| 		$filters[$table]['or'][] = $this->convertFilter($key[1],'eq',$key[0]);
 | |
| 		$this->addWhereFromFilters($filters[$table],$sql,$params);
 | |
| 		$object = null;
 | |
| 		if ($result = $this->db->query($sql,$params)) {
 | |
| 			$object = $this->db->fetchAssoc($result);
 | |
| 			$this->db->close($result);
 | |
| 		}
 | |
| 		return $object;
 | |
| 	}
 | |
| 
 | |
| 	protected function createObject($input,$tables) {
 | |
| 		if (!$input) return false;
 | |
| 		$input = (array)$input;
 | |
| 		$keys = implode(',',str_split(str_repeat('!', count($input))));
 | |
| 		$values = implode(',',str_split(str_repeat('?', count($input))));
 | |
| 		$params = array_merge(array_keys($input),array_values($input));
 | |
| 		array_unshift($params, $tables[0]);
 | |
| 		$result = $this->db->query('INSERT INTO ! ('.$keys.') VALUES ('.$values.')',$params);
 | |
| 		if (!$result) return null;
 | |
| 		return $this->db->insertId($result);
 | |
| 	}
 | |
| 
 | |
| 	protected function createObjects($inputs,$tables) {
 | |
| 		if (!$inputs) return false;
 | |
| 		$ids = array();
 | |
| 		$this->db->query('BEGIN',array());
 | |
| 		foreach ($inputs as $input) {
 | |
| 			$input = (array)$input;
 | |
| 			$keys = implode(',',str_split(str_repeat('!', count($input))));
 | |
| 			$values = implode(',',str_split(str_repeat('?', count($input))));
 | |
| 			$params = array_merge(array_keys($input),array_values($input));
 | |
| 			array_unshift($params, $tables[0]);
 | |
| 			$result = $this->db->query('INSERT INTO ! ('.$keys.') VALUES ('.$values.')',$params);
 | |
| 			if (!$result) {
 | |
| 				$this->db->query('ROLLBACK',array());
 | |
| 				return null;
 | |
| 			}
 | |
| 			$ids[] = $this->db->insertId($result);
 | |
| 		}
 | |
| 		$this->db->query('COMMIT',array());
 | |
| 		return $ids;
 | |
| 	}
 | |
| 
 | |
| 	protected function updateObject($key,$input,$filters,$tables) {
 | |
| 		if (!$input) return false;
 | |
| 		$input = (array)$input;
 | |
| 		$table = $tables[0];
 | |
| 		$sql = 'UPDATE ! SET ';
 | |
| 		$params = array($table);
 | |
| 		foreach (array_keys($input) as $i=>$k) {
 | |
| 			if ($i) $sql .= ',';
 | |
| 			$v = $input[$k];
 | |
| 			$sql .= '!=?';
 | |
| 			$params[] = $k;
 | |
| 			$params[] = $v;
 | |
| 		}
 | |
| 		if (!isset($filters[$table])) $filters[$table] = array();
 | |
| 		if (!isset($filters[$table]['or'])) $filters[$table]['or'] = array();
 | |
| 		$filters[$table]['or'][] = $this->convertFilter($key[1],'eq',$key[0]);
 | |
| 		$this->addWhereFromFilters($filters[$table],$sql,$params);
 | |
| 		$result = $this->db->query($sql,$params);
 | |
| 		if (!$result) return null;
 | |
| 		return $this->db->affectedRows($result);
 | |
| 	}
 | |
| 
 | |
| 	protected function deleteObject($key,$filters,$tables) {
 | |
| 		$table = $tables[0];
 | |
| 		$sql = 'DELETE FROM !';
 | |
| 		$params = array($table);
 | |
| 		if (!isset($filters[$table])) $filters[$table] = array();
 | |
| 		if (!isset($filters[$table]['or'])) $filters[$table]['or'] = array();
 | |
| 		$filters[$table]['or'][] = $this->convertFilter($key[1],'eq',$key[0]);
 | |
| 		$this->addWhereFromFilters($filters[$table],$sql,$params);
 | |
| 		$result = $this->db->query($sql,$params);
 | |
| 		if (!$result) return null;
 | |
| 		return $this->db->affectedRows($result);
 | |
| 	}
 | |
| 
 | |
| 	protected function findRelations($tables,$database,$auto_include) {
 | |
| 		$tableset = array();
 | |
| 		$collect = array();
 | |
| 		$select = array();
 | |
| 
 | |
| 		while (count($tables)>1) {
 | |
| 			$table0 = array_shift($tables);
 | |
| 			$tableset[] = $table0;
 | |
| 
 | |
| 			$result = $this->db->query($this->db->getSql('reflect_belongs_to'),array($table0,$tables,$database,$database));
 | |
| 			while ($row = $this->db->fetchRow($result)) {
 | |
| 				if (!$auto_include && !in_array($row[0],array_merge($tables,$tableset))) continue;
 | |
| 				$collect[$row[0]][$row[1]]=array();
 | |
| 				$select[$row[2]][$row[3]]=array($row[0],$row[1]);
 | |
| 				if (!in_array($row[0],$tableset)) $tableset[] = $row[0];
 | |
| 			}
 | |
| 			$result = $this->db->query($this->db->getSql('reflect_has_many'),array($tables,$table0,$database,$database));
 | |
| 			while ($row = $this->db->fetchRow($result)) {
 | |
| 				if (!$auto_include && !in_array($row[2],array_merge($tables,$tableset))) continue;
 | |
| 				$collect[$row[2]][$row[3]]=array();
 | |
| 				$select[$row[0]][$row[1]]=array($row[2],$row[3]);
 | |
| 				if (!in_array($row[2],$tableset)) $tableset[] = $row[2];
 | |
| 			}
 | |
| 			$result = $this->db->query($this->db->getSql('reflect_habtm'),array($database,$database,$database,$database,$table0,$tables));
 | |
| 			while ($row = $this->db->fetchRow($result)) {
 | |
| 				if (!$auto_include && !in_array($row[2],array_merge($tables,$tableset))) continue;
 | |
| 				if (!$auto_include && !in_array($row[4],array_merge($tables,$tableset))) continue;
 | |
| 				$collect[$row[2]][$row[3]]=array();
 | |
| 				$select[$row[0]][$row[1]]=array($row[2],$row[3]);
 | |
| 				$collect[$row[4]][$row[5]]=array();
 | |
| 				$select[$row[6]][$row[7]]=array($row[4],$row[5]);
 | |
| 				if (!in_array($row[2],$tableset)) $tableset[] = $row[2];
 | |
| 				if (!in_array($row[4],$tableset)) $tableset[] = $row[4];
 | |
| 			}
 | |
| 		}
 | |
| 		$tableset[] = array_shift($tables);
 | |
| 		$tableset = array_unique($tableset);
 | |
| 		return array($tableset,$collect,$select);
 | |
| 	}
 | |
| 
 | |
| 	protected function retrieveInput($post) {
 | |
| 		$input = (object)array();
 | |
| 		$data = trim(file_get_contents($post));
 | |
| 		if (strlen($data)>0) {
 | |
| 			if ($data[0]=='{' || $data[0]=='[') {
 | |
| 				$input = json_decode($data);
 | |
| 			} else {
 | |
| 				parse_str($data, $input);
 | |
| 				foreach ($input as $key => $value) {
 | |
| 					if (substr($key,-9)=='__is_null') {
 | |
| 						$input[substr($key,0,-9)] = null;
 | |
| 						unset($input[$key]);
 | |
| 					}
 | |
| 				}
 | |
| 				$input = (object)$input;
 | |
| 			}
 | |
| 		}
 | |
| 		return is_array($input)?$input:array($input);
 | |
| 	}
 | |
| 
 | |
| 	protected function addRelationColumns($columns,$select) {
 | |
| 		if ($columns) {
 | |
| 			foreach ($select as $table=>$keys) {
 | |
| 				foreach ($keys as $key=>$other) {
 | |
| 					$columns.=",$table.$key,".implode('.',$other);
 | |
| 				}
 | |
| 		  }
 | |
| 		}
 | |
| 		return $columns;
 | |
| 	}
 | |
| 
 | |
| 	protected function findFields($tables,$columns,$database) {
 | |
| 		$fields = array();
 | |
| 		foreach ($tables as $i=>$table) {
 | |
| 			$fields[$table] = $this->findTableFields($table,$database);
 | |
| 			$fields[$table] = $this->filterFieldsByColumns($fields[$table],$columns,$i==0,$table);
 | |
| 		}
 | |
| 		return $fields;
 | |
| 	}
 | |
| 
 | |
| 	protected function filterFieldsByColumns($fields,$columns,$first,$table) {
 | |
| 		if ($columns) {
 | |
| 			$columns = explode(',',$columns);
 | |
| 			foreach (array_keys($fields) as $key) {
 | |
| 				$delete = true;
 | |
| 				foreach ($columns as $column) {
 | |
| 					if (strpos($column,'.')) {
 | |
| 						if ($column=="$table.$key" || $column=="$table.*") {
 | |
| 							$delete = false;
 | |
| 						}
 | |
| 					} elseif ($first) {
 | |
| 						if ($column==$key || $column=="*") {
 | |
| 							$delete = false;
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				if ($delete) unset($fields[$key]);
 | |
| 			}
 | |
| 		}
 | |
| 		return $fields;
 | |
| 	}
 | |
| 
 | |
| 	protected function findTableFields($table,$database) {
 | |
| 		$fields = array();
 | |
| 		foreach ($this->db->fetchFields($table) as $field) {
 | |
| 			$fields[$field->name] = $field;
 | |
| 		}
 | |
| 		return $fields;
 | |
| 	}
 | |
| 
 | |
| 	protected function filterInputByFields($input,$fields) {
 | |
| 		if ($fields) foreach (array_keys((array)$input) as $key) {
 | |
| 			if (!isset($fields[$key])) {
 | |
| 				unset($input->$key);
 | |
| 			}
 | |
| 		}
 | |
| 		return $input;
 | |
| 	}
 | |
| 
 | |
| 	protected function convertInputs(&$input,$fields) {
 | |
| 		foreach ($fields as $key=>$field) {
 | |
| 			if (isset($input->$key) && $input->$key && $this->db->isBinaryType($field)) {
 | |
| 				$value = $input->$key;
 | |
| 				$value = str_pad(strtr($value, '-_', '+/'), ceil(strlen($value) / 4) * 4, '=', STR_PAD_RIGHT);
 | |
| 				$input->$key = (object)array('type'=>'base64','value'=>$value);
 | |
| 			}
 | |
| 			if (isset($input->$key) && $input->$key && $this->db->isGeometryType($field)) {
 | |
| 				$input->$key = (object)array('type'=>'wkt','value'=>$input->$key);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function convertOutputs(&$sql, &$params, $fields) {
 | |
| 		$sql .= implode(',',str_split(str_repeat('!',count($fields))));
 | |
| 		foreach ($fields as $key=>$field) {
 | |
| 			if ($this->db->isBinaryType($field)) {
 | |
| 				$params[] = (object)array('type'=>'base64','key'=>$key);
 | |
| 			}
 | |
| 			else if ($this->db->isGeometryType($field)) {
 | |
| 				$params[] = (object)array('type'=>'wkt','key'=>$key);
 | |
| 			}
 | |
| 			else {
 | |
| 				$params[] = $key;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function getParameters($settings) {
 | |
| 		extract($settings);
 | |
| 
 | |
| 		$table     = $this->parseRequestParameter($request, 'a-zA-Z0-9\-_');
 | |
| 		$key       = $this->parseRequestParameter($request, 'a-zA-Z0-9\-_'); // auto-increment or uuid
 | |
| 		$action    = $this->mapMethodToAction($method,$key);
 | |
| 		$include   = $this->parseGetParameter($get, 'include', 'a-zA-Z0-9\-_,');
 | |
| 		$callback  = $this->parseGetParameter($get, 'callback', 'a-zA-Z0-9\-_');
 | |
| 		$page      = $this->parseGetParameter($get, 'page', '0-9,');
 | |
| 		$filters   = $this->parseGetParameterArray($get, 'filter', false);
 | |
| 		$satisfy   = $this->parseGetParameter($get, 'satisfy', 'a-zA-Z');
 | |
| 		$columns   = $this->parseGetParameter($get, 'columns', 'a-zA-Z0-9\-_,.*');
 | |
| 		$order     = $this->parseGetParameter($get, 'order', 'a-zA-Z0-9\-_,');
 | |
| 		$transform = $this->parseGetParameter($get, 'transform', 't1');
 | |
| 
 | |
| 		$tables    = $this->processTableAndIncludeParameters($database,$table,$include,$action);
 | |
| 		$key       = $this->processKeyParameter($key,$tables,$database);
 | |
| 		$filters   = $this->processFiltersParameter($tables,$satisfy,$filters);
 | |
| 		$page      = $this->processPageParameter($page);
 | |
| 		$order     = $this->processOrderParameter($order);
 | |
| 
 | |
| 		// reflection
 | |
| 		list($tables,$collect,$select) = $this->findRelations($tables,$database,$auto_include);
 | |
| 		$columns = $this->addRelationColumns($columns,$select);
 | |
| 		$fields = $this->findFields($tables,$columns,$database);
 | |
| 
 | |
| 		// permissions
 | |
| 		if ($table_authorizer) $this->applyTableAuthorizer($table_authorizer,$action,$database,$tables);
 | |
| 		if (!isset($tables[0])) $this->exitWith404('entity');
 | |
| 		if ($record_filter) $this->applyRecordFilter($record_filter,$action,$database,$tables,$filters);
 | |
| 		if ($tenancy_function) $this->applyTenancyFunction($tenancy_function,$action,$database,$fields,$filters);
 | |
| 		if ($column_authorizer) $this->applyColumnAuthorizer($column_authorizer,$action,$database,$fields);
 | |
| 
 | |
| 		if ($post) {
 | |
| 			// input
 | |
| 			$contexts = $this->retrieveInput($post);
 | |
| 			$inputs = array();
 | |
| 			foreach ($contexts as $context) {
 | |
| 				$input = $this->filterInputByFields($context,$fields[$tables[0]]);
 | |
| 
 | |
| 				if ($tenancy_function) $this->applyInputTenancy($tenancy_function,$action,$database,$tables[0],$input,$fields[$tables[0]]);
 | |
| 				if ($input_sanitizer) $this->applyInputSanitizer($input_sanitizer,$action,$database,$tables[0],$input,$fields[$tables[0]]);
 | |
| 				if ($input_validator) $this->applyInputValidator($input_validator,$action,$database,$tables[0],$input,$fields[$tables[0]],$context);
 | |
| 
 | |
| 				$this->convertInputs($input,$fields[$tables[0]]);
 | |
| 				$inputs[] = $input;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return compact('action','database','tables','key','callback','page','filters','fields','order','transform','inputs','collect','select');
 | |
| 	}
 | |
| 
 | |
| 	protected function addWhereFromFilters($filters,&$sql,&$params) {
 | |
| 		$first = true;
 | |
| 		if (isset($filters['or'])) {
 | |
| 			$first = false;
 | |
| 			$sql .= ' WHERE (';
 | |
| 			foreach ($filters['or'] as $i=>$filter) {
 | |
| 				$sql .= $i==0?'':' OR ';
 | |
| 				$sql .= $filter[0];
 | |
| 				for ($i=1;$i<count($filter);$i++) {
 | |
| 					$params[] = $filter[$i];
 | |
| 				}
 | |
| 			}
 | |
| 			$sql .= ')';
 | |
| 		}
 | |
| 		if (isset($filters['and'])) {
 | |
| 			foreach ($filters['and'] as $i=>$filter) {
 | |
| 				$sql .= $first?' WHERE ':' AND ';
 | |
| 				$sql .= $filter[0];
 | |
| 				for ($i=1;$i<count($filter);$i++) {
 | |
| 					$params[] = $filter[$i];
 | |
| 				}
 | |
| 				$first = false;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected function listCommandInternal($parameters) {
 | |
| 		extract($parameters);
 | |
| 		echo '{';
 | |
| 		$table = array_shift($tables);
 | |
| 		// first table
 | |
| 		$count = false;
 | |
| 		echo '"'.$table.'":{';
 | |
| 		if (is_array($order) && is_array($page)) {
 | |
| 			$params = array();
 | |
| 			$sql = 'SELECT COUNT(*) FROM !';
 | |
| 			$params[] = $table;
 | |
| 			if (isset($filters[$table])) {
 | |
| 					$this->addWhereFromFilters($filters[$table],$sql,$params);
 | |
| 			}
 | |
| 			if ($result = $this->db->query($sql,$params)) {
 | |
| 				while ($pages = $this->db->fetchRow($result)) {
 | |
| 					$count = $pages[0];
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		$params = array();
 | |
| 		$sql = 'SELECT ';
 | |
| 		$this->convertOutputs($sql,$params,$fields[$table]);
 | |
| 		$sql .= ' FROM !';
 | |
| 		$params[] = $table;
 | |
| 		if (isset($filters[$table])) {
 | |
| 				$this->addWhereFromFilters($filters[$table],$sql,$params);
 | |
| 		}
 | |
| 		if (is_array($order)) {
 | |
| 			$sql .= ' ORDER BY ! '.$order[1];
 | |
| 			$params[] = $order[0];
 | |
| 		}
 | |
| 		if (is_array($order) && is_array($page)) {
 | |
| 			$sql = $this->db->addLimitToSql($sql,$page[1],$page[0]);
 | |
| 		}
 | |
| 		if ($result = $this->db->query($sql,$params)) {
 | |
| 			echo '"columns":';
 | |
| 			$keys = array_keys($fields[$table]);
 | |
| 			echo json_encode($keys);
 | |
| 			$keys = array_flip($keys);
 | |
| 			echo ',"records":[';
 | |
| 			$first_row = true;
 | |
| 			while ($row = $this->db->fetchRow($result)) {
 | |
| 				if ($first_row) $first_row = false;
 | |
| 				else echo ',';
 | |
| 				if (isset($collect[$table])) {
 | |
| 					foreach (array_keys($collect[$table]) as $field) {
 | |
| 						$collect[$table][$field][] = $row[$keys[$field]];
 | |
| 					}
 | |
| 				}
 | |
| 				echo json_encode($row);
 | |
| 			}
 | |
| 			$this->db->close($result);
 | |
| 			echo ']';
 | |
| 			if ($count) echo ',';
 | |
| 		}
 | |
| 		if ($count) echo '"results":'.$count;
 | |
| 		echo '}';
 | |
| 		// other tables
 | |
| 		foreach ($tables as $t=>$table) {
 | |
| 			echo ',';
 | |
| 			echo '"'.$table.'":{';
 | |
| 			$params = array();
 | |
| 			$sql = 'SELECT ';
 | |
| 			$this->convertOutputs($sql,$params,$fields[$table]);
 | |
| 			$sql .= ' FROM !';
 | |
| 			$params[] = $table;
 | |
| 			if (isset($select[$table])) {
 | |
| 				echo '"relations":{';
 | |
| 				$first_row = true;
 | |
| 				foreach ($select[$table] as $field => $path) {
 | |
| 					$values = $collect[$path[0]][$path[1]];
 | |
| 					if ($values) {
 | |
| 						if (!isset($filters[$table])) $filters[$table] = array();
 | |
| 						if (!isset($filters[$table]['or'])) $filters[$table]['or'] = array();
 | |
| 						$filters[$table]['or'][] = $this->convertFilter($field,'in',implode(',',$values));
 | |
| 					}
 | |
| 					if ($first_row) $first_row = false;
 | |
| 					else echo ',';
 | |
| 					echo '"'.$field.'":"'.implode('.',$path).'"';
 | |
| 				}
 | |
| 				echo '}';
 | |
| 				if (isset($filters[$table])) {
 | |
| 					$this->addWhereFromFilters($filters[$table],$sql,$params);
 | |
| 				}
 | |
| 			}
 | |
| 			if ($result = $this->db->query($sql,$params)) {
 | |
| 				if (isset($select[$table])) echo ',';
 | |
| 				echo '"columns":';
 | |
| 				$keys = array_keys($fields[$table]);
 | |
| 				echo json_encode($keys);
 | |
| 				$keys = array_flip($keys);
 | |
| 				echo ',"records":[';
 | |
| 				$first_row = true;
 | |
| 				while ($row = $this->db->fetchRow($result)) {
 | |
| 					if ($first_row) $first_row = false;
 | |
| 					else echo ',';
 | |
| 					if (isset($collect[$table])) {
 | |
| 						foreach (array_keys($collect[$table]) as $field) {
 | |
| 							$collect[$table][$field][]=$row[$keys[$field]];
 | |
| 						}
 | |
| 					}
 | |
| 					echo json_encode($row);
 | |
| 				}
 | |
| 				$this->db->close($result);
 | |
| 				echo ']';
 | |
| 			}
 | |
| 			echo '}';
 | |
| 		}
 | |
| 		echo '}';
 | |
| 	}
 | |
| 
 | |
| 	protected function readCommand($parameters) {
 | |
| 		extract($parameters);
 | |
| 		$object = $this->retrieveObject($key,$fields,$filters,$tables);
 | |
| 		if (!$object) $this->exitWith404('object');
 | |
| 		$this->startOutput($callback);
 | |
| 		echo json_encode($object);
 | |
| 		$this->endOutput($callback);
 | |
| 	}
 | |
| 
 | |
| 	protected function createCommand($parameters) {
 | |
| 		extract($parameters);
 | |
| 		if (!$inputs || !$inputs[0]) $this->exitWith404('input');
 | |
| 		$this->startOutput($callback);
 | |
| 		if (count($inputs)==1) echo json_encode($this->createObject($inputs[0],$tables));
 | |
| 		else echo json_encode($this->createObjects($inputs,$tables));
 | |
| 		$this->endOutput($callback);
 | |
| 	}
 | |
| 
 | |
| 	protected function updateCommand($parameters) {
 | |
| 		extract($parameters);
 | |
| 		if (!$inputs) $this->exitWith404('subject');
 | |
| 		$this->startOutput($callback);
 | |
| 		echo json_encode($this->updateObject($key,$inputs[0],$filters,$tables));
 | |
| 		$this->endOutput($callback);
 | |
| 	}
 | |
| 
 | |
| 	protected function deleteCommand($parameters) {
 | |
| 		extract($parameters);
 | |
| 		$this->startOutput($callback);
 | |
| 		echo json_encode($this->deleteObject($key,$filters,$tables));
 | |
| 		$this->endOutput($callback);
 | |
| 	}
 | |
| 
 | |
| 	protected function listCommand($parameters) {
 | |
| 		extract($parameters);
 | |
| 		$this->startOutput($callback);
 | |
| 		if ($transform) {
 | |
| 			ob_start();
 | |
| 		}
 | |
| 		$this->listCommandInternal($parameters);
 | |
| 		if ($transform) {
 | |
| 			$content = ob_get_contents();
 | |
| 			ob_end_clean();
 | |
| 			$data = json_decode($content,true);
 | |
| 			echo json_encode(self::php_crud_api_transform($data));
 | |
| 		}
 | |
| 		$this->endOutput($callback);
 | |
| 	}
 | |
| 
 | |
| 	public function __construct($config) {
 | |
| 		extract($config);
 | |
| 
 | |
| 		// initialize
 | |
| 		$dbengine = isset($dbengine)?$dbengine:null;
 | |
| 		$hostname = isset($hostname)?$hostname:null;
 | |
| 		$username = isset($username)?$username:null;
 | |
| 		$password = isset($password)?$password:null;
 | |
| 		$database = isset($database)?$database:null;
 | |
| 		$port = isset($port)?$port:null;
 | |
| 		$socket = isset($socket)?$socket:null;
 | |
| 		$charset = isset($charset)?$charset:null;
 | |
| 
 | |
| 		$table_authorizer = isset($table_authorizer)?$table_authorizer:null;
 | |
| 		$record_filter = isset($record_filter)?$record_filter:null;
 | |
| 		$column_authorizer = isset($column_authorizer)?$column_authorizer:null;
 | |
| 		$tenancy_function = isset($tenancy_function)?$tenancy_function:null;
 | |
| 		$input_sanitizer = isset($input_sanitizer)?$input_sanitizer:null;
 | |
| 		$input_validator = isset($input_validator)?$input_validator:null;
 | |
| 		$extensions = isset($extensions)?$extensions:null;
 | |
| 		$auto_include = isset($auto_include)?$auto_include:null;
 | |
| 
 | |
| 		$db = isset($db)?$db:null;
 | |
| 		$method = isset($method)?$method:null;
 | |
| 		$request = isset($request)?$request:null;
 | |
| 		$get = isset($get)?$get:null;
 | |
| 		$post = isset($post)?$post:null;
 | |
| 
 | |
| 		// defaults
 | |
| 		if (!$dbengine) {
 | |
| 			$dbengine = 'MySQL';
 | |
| 		}
 | |
| 		if (!$method) {
 | |
| 			$method = $_SERVER['REQUEST_METHOD'];
 | |
| 		}
 | |
| 		if (!$request) {
 | |
| 			$request = isset($_SERVER['PATH_INFO'])?$_SERVER['PATH_INFO']:'';
 | |
| 			if (!$request) {
 | |
| 				$request = isset($_SERVER['ORIG_PATH_INFO'])?$_SERVER['ORIG_PATH_INFO']:'';
 | |
| 			}
 | |
| 		}
 | |
| 		if (!$get) {
 | |
| 			$get = $_GET;
 | |
| 		}
 | |
| 		if (!$post) {
 | |
| 			$post = 'php://input';
 | |
| 		}
 | |
| 
 | |
| 		// connect
 | |
| 		$request = trim($request,'/');
 | |
| 		if (!$database) {
 | |
| 			$database  = $this->parseRequestParameter($request, 'a-zA-Z0-9\-_');
 | |
| 		}
 | |
| 		if (!$db) {
 | |
| 			$db = new $dbengine();
 | |
| 			if (!$charset) {
 | |
| 				$charset = $db->getDefaultCharset();
 | |
| 			}
 | |
| 			$db->connect($hostname,$username,$password,$database,$port,$socket,$charset);
 | |
| 		}
 | |
| 		if ($extensions===null) {
 | |
| 			$extensions = true;
 | |
| 		}
 | |
| 		if ($auto_include===null) {
 | |
| 			$auto_include = true;
 | |
| 		}
 | |
| 
 | |
| 		$this->db = $db;
 | |
| 		$this->settings = compact('method', 'request', 'get', 'post', 'database', 'table_authorizer', 'record_filter', 'column_authorizer', 'tenancy_function', 'input_sanitizer', 'input_validator', 'extensions', 'auto_include');
 | |
| 	}
 | |
| 
 | |
| 	public static function php_crud_api_transform(&$tables) {
 | |
| 		$get_objects = function (&$tables,$table_name,$where_index=false,$match_value=false) use (&$get_objects) {
 | |
| 			$objects = array();
 | |
| 			if (isset($tables[$table_name]['records'])) {
 | |
| 				foreach ($tables[$table_name]['records'] as $record) {
 | |
| 					if ($where_index===false || $record[$where_index]==$match_value) {
 | |
| 						$object = array();
 | |
| 						foreach ($tables[$table_name]['columns'] as $index=>$column) {
 | |
| 							$object[$column] = $record[$index];
 | |
| 							foreach ($tables as $relation=>$reltable) {
 | |
| 								if (isset($reltable['relations'])) {
 | |
| 									foreach ($reltable['relations'] as $key=>$target) {
 | |
| 										if ($target == "$table_name.$column") {
 | |
| 											$column_indices = array_flip($reltable['columns']);
 | |
| 											$object[$relation] = $get_objects($tables,$relation,$column_indices[$key],$record[$index]);
 | |
| 										}
 | |
| 									}
 | |
| 								}
 | |
| 							}
 | |
| 						}
 | |
| 						$objects[] = $object;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			return $objects;
 | |
| 		};
 | |
| 		$tree = array();
 | |
| 		foreach ($tables as $name=>$table) {
 | |
| 			if (!isset($table['relations'])) {
 | |
| 				$tree[$name] = $get_objects($tables,$name);
 | |
| 				if (isset($table['results'])) {
 | |
| 					$tree['_results'] = $table['results'];
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return $tree;
 | |
| 	}
 | |
| 
 | |
| 	protected function swagger($settings) {
 | |
| 		extract($settings);
 | |
| 
 | |
| 		$tables = array();
 | |
| 		if ($result = $this->db->query($this->db->getSql('list_tables'),array($database))) {
 | |
| 			while ($row = $this->db->fetchRow($result)) {
 | |
| 				$table = array(
 | |
| 					'name'=>$row[0],
 | |
| 					'comments'=>$row[1],
 | |
| 					'root_actions'=>array(
 | |
| 						array('name'=>'list','method'=>'get'),
 | |
| 						array('name'=>'create','method'=>'post'),
 | |
| 					),
 | |
| 					'id_actions'=>array(
 | |
| 						array('name'=>'read','method'=>'get'),
 | |
| 						array('name'=>'update','method'=>'put'),
 | |
| 						array('name'=>'delete','method'=>'delete'),
 | |
| 					),
 | |
| 				);
 | |
| 				$tables[] = $table;
 | |
| 			}
 | |
| 			$this->db->close($result);
 | |
| 		}
 | |
| 
 | |
| 		foreach ($tables as $t=>$table)	{
 | |
| 			$table_list = array($table['name']);
 | |
| 			$table_fields = $this->findFields($table_list,false,$database);
 | |
| 			$table_names = array_map(function($v){ return $v['name'];},$tables);
 | |
| 			
 | |
| 			if ($extensions) {
 | |
| 				$result = $this->db->query($this->db->getSql('reflect_belongs_to'),array($table_list[0],$table_names,$database,$database));
 | |
| 				while ($row = $this->db->fetchRow($result)) {
 | |
| 					$table_fields[$table['name']][$row[1]]->references=array($row[2],$row[3]);
 | |
| 				}
 | |
| 				$result = $this->db->query($this->db->getSql('reflect_has_many'),array($table_names,$table_list[0],$database,$database));
 | |
| 				while ($row = $this->db->fetchRow($result)) {
 | |
| 					$table_fields[$table['name']][$row[3]]->referenced[]=array($row[0],$row[1]);
 | |
| 				}
 | |
| 				$primaryKeys = $this->findPrimaryKeys($table_list[0],$database);
 | |
| 				foreach ($primaryKeys as $primaryKey) {
 | |
| 					$table_fields[$table['name']][$primaryKey]->primaryKey = true;
 | |
| 				}
 | |
| 			}
 | |
| 			
 | |
| 			foreach (array('root_actions','id_actions') as $path) {
 | |
| 				foreach ($table[$path] as $i=>$action) {
 | |
| 					$table_list = array($table['name']);
 | |
| 					$fields = $table_fields;
 | |
| 					if ($table_authorizer) $this->applyTableAuthorizer($table_authorizer,$action['name'],$database,$table_list);
 | |
| 					if ($column_authorizer) $this->applyColumnAuthorizer($column_authorizer,$action['name'],$database,$fields);
 | |
| 					if (!$table_list || !$fields[$table['name']]) $tables[$t][$path][$i] = false;
 | |
| 					else $tables[$t][$path][$i]['fields'] = $fields[$table['name']];
 | |
| 				}
 | |
| 				// remove unauthorized tables and tables without fields
 | |
| 				$tables[$t][$path] = array_values(array_filter($tables[$t][$path]));
 | |
| 			}
 | |
| 			if (!$tables[$t]['root_actions']&&!$tables[$t]['id_actions']) $tables[$t] = false;
 | |
| 		}
 | |
| 		$tables = array_merge(array_filter($tables));
 | |
| 		//var_dump($tables);die();
 | |
| 
 | |
| 		header('Content-Type: application/json; charset=utf-8');
 | |
| 		echo '{"swagger":"2.0",';
 | |
| 		echo '"info":{';
 | |
| 		echo '"title":"'.$database.'",';
 | |
| 		echo '"description":"API generated with [PHP-CRUD-API](https://github.com/mevdschee/php-crud-api)",';
 | |
| 		echo '"version":"1.0.0"';
 | |
| 		echo '},';
 | |
| 		echo '"host":"'.$_SERVER['HTTP_HOST'].'",';
 | |
| 		echo '"basePath":"'.$_SERVER['SCRIPT_NAME'].'",';
 | |
| 		echo '"schemes":["http'.((!empty($_SERVER['HTTPS'])&&$_SERVER['HTTPS']!=='off')?'s':'').'"],';
 | |
| 		echo '"consumes":["application/json"],';
 | |
| 		echo '"produces":["application/json"],';
 | |
| 		echo '"tags":[';
 | |
| 		foreach ($tables as $i=>$table) {
 | |
| 			if ($i>0) echo ',';
 | |
| 			echo '{';
 | |
| 			echo '"name":"'.$table['name'].'",';
 | |
| 			echo '"description":"'.$table['comments'].'"';
 | |
| 			echo '}';
 | |
| 		}
 | |
| 		echo '],';
 | |
| 		echo '"paths":{';
 | |
| 		foreach ($tables as $i=>$table) {
 | |
| 			if ($table['root_actions']) {
 | |
| 				if ($i>0) echo ',';
 | |
| 				echo '"/'.$table['name'].'":{';
 | |
| 				foreach ($table['root_actions'] as $j=>$action) {
 | |
| 					if ($j>0) echo ',';
 | |
| 					echo '"'.$action['method'].'":{';
 | |
| 					echo '"tags":["'.$table['name'].'"],';
 | |
| 					echo '"summary":"'.ucfirst($action['name']).'",';
 | |
| 					if ($action['name']=='list') {
 | |
| 						echo '"parameters":[';
 | |
| 						echo '{';
 | |
| 						echo '"name":"include",';
 | |
| 						echo '"in":"query",';
 | |
| 						echo '"description":"One or more related entities (comma separated).",';
 | |
| 						echo '"required":false,';
 | |
| 						echo '"type":"string"';
 | |
| 						echo '},';
 | |
| 						echo '{';
 | |
| 						echo '"name":"order",';
 | |
| 						echo '"in":"query",';
 | |
| 						echo '"description":"Column you want to sort on and the sort direction (comma separated). Example: id,desc",';
 | |
| 						echo '"required":false,';
 | |
| 						echo '"type":"string"';
 | |
| 						echo '},';
 | |
| 						echo '{';
 | |
| 						echo '"name":"page",';
 | |
| 						echo '"in":"query",';
 | |
| 						echo '"description":"Page number and page size (comma separated). NB: You cannot use \"page\" without \"order\"! Example: 1,10",';
 | |
| 						echo '"required":false,';
 | |
| 						echo '"type":"string"';
 | |
| 						echo '},';
 | |
| 						echo '{';
 | |
| 						echo '"name":"transform",';
 | |
| 						echo '"in":"query",';
 | |
| 						echo '"description":"Transform the records to object format. NB: This can also be done client-side in JavaScript!",';
 | |
| 						echo '"required":false,';
 | |
| 						echo '"type":"boolean"';
 | |
| 						echo '},';
 | |
| 						echo '{';
 | |
| 						echo '"name":"columns",';
 | |
| 						echo '"in":"query",';
 | |
| 						echo '"description":"The table columns you want to retrieve (comma separated). Example: posts.*,categories.name",';
 | |
| 						echo '"required":false,';
 | |
| 						echo '"type":"string"';
 | |
| 						echo '},';
 | |
| 						echo '{';
 | |
| 						echo '"name":"filter[]",';
 | |
| 						echo '"in":"query",';
 | |
| 						echo '"description":"Filters to be applied. Each filter consists of a column, an operator and a value (comma separated). Example: id,eq,1",';
 | |
| 						echo '"required":false,';
 | |
| 						echo '"type":"array",';
 | |
| 						echo '"collectionFormat":"multi",';
 | |
| 						echo '"items":{"type":"string"}';
 | |
| 						echo '},';
 | |
| 						echo '{';
 | |
| 						echo '"name":"satisfy",';
 | |
| 						echo '"in":"query",';
 | |
| 						echo '"description":"Should all filters match (default)? Or any?",';
 | |
| 						echo '"required":false,';
 | |
| 						echo '"type":"string",';
 | |
| 						echo '"enum":["any"]';
 | |
| 						echo '},';
 | |
| 						echo '{';
 | |
| 						echo '"name":"callback",';
 | |
| 						echo '"in":"query",';
 | |
| 						echo '"description":"JSONP callback function name",';
 | |
| 						echo '"required":false,';
 | |
| 						echo '"type":"string"';
 | |
| 						echo '}';
 | |
| 						echo '],';
 | |
| 						echo '"responses":{';
 | |
| 						echo '"200":{';
 | |
| 						echo '"description":"An array of '.$table['name'].'",';
 | |
| 						echo '"schema":{';
 | |
| 						echo '"type":"array",';
 | |
| 						echo '"items":{';
 | |
| 						echo '"type": "object",';
 | |
| 						echo '"properties": {';
 | |
| 						foreach (array_keys($action['fields']) as $k=>$field) {
 | |
| 							if ($k>0) echo ',';
 | |
| 							echo '"'.$field.'": {';
 | |
| 							echo '"type": "string"';
 | |
| 							if (isset($action['fields'][$field]->referenced)) {
 | |
| 								echo ',"x-referenced": '.json_encode($action['fields'][$field]->referenced);
 | |
| 							}
 | |
| 							if (isset($action['fields'][$field]->references)) {
 | |
| 								echo ',"x-references": '.json_encode($action['fields'][$field]->references);
 | |
| 							}
 | |
| 							if (isset($action['fields'][$field]->primaryKey)) {
 | |
| 								echo ',"x-primary-key": true';
 | |
| 							}
 | |
| 							echo '}';
 | |
| 						}
 | |
| 						echo '}'; //properties
 | |
| 						echo '}'; //items
 | |
| 						echo '}'; //schema
 | |
| 						echo '}'; //200
 | |
| 						echo '}'; //responses
 | |
| 					}
 | |
| 					if ($action['name']=='create') {
 | |
| 						echo '"parameters":[{';
 | |
| 						echo '"name":"item",';
 | |
| 						echo '"in":"body",';
 | |
| 						echo '"description":"Item to create.",';
 | |
| 						echo '"required":false,';
 | |
| 						echo '"schema":{';
 | |
| 						echo '"type": "object",';
 | |
| 						echo '"properties": {';
 | |
| 						foreach (array_keys($action['fields']) as $k=>$field) {
 | |
| 							if ($k>0) echo ',';
 | |
| 							echo '"'.$field.'": {';
 | |
| 							echo '"type": "string"';
 | |
| 							if (isset($action['fields'][$field]->referenced)) {
 | |
| 								echo ',"x-referenced": '.json_encode($action['fields'][$field]->referenced);
 | |
| 							}
 | |
| 							if (isset($action['fields'][$field]->references)) {
 | |
| 								echo ',"x-references": '.json_encode($action['fields'][$field]->references);
 | |
| 							}
 | |
| 							if (isset($action['fields'][$field]->primaryKey)) {
 | |
| 								echo ',"x-primary-key": true';
 | |
| 							}
 | |
| 							echo '}';
 | |
| 						}
 | |
| 						echo '}'; //properties
 | |
| 						echo '}'; //schema
 | |
| 						echo '}],';
 | |
| 						echo '"responses":{';
 | |
| 						echo '"200":{';
 | |
| 						echo '"description":"Identifier of created item.",';
 | |
| 						echo '"schema":{';
 | |
| 						echo '"type":"integer"';
 | |
| 						echo '}';//schema
 | |
| 						echo '}';//200
 | |
| 						echo '}';//responses
 | |
| 					}
 | |
| 					echo '}';//method
 | |
| 				}
 | |
| 				echo '}';
 | |
| 			}
 | |
| 			if ($table['id_actions']) {
 | |
| 				if ($i>0 || $table['root_actions']) echo ',';
 | |
| 				echo '"/'.$table['name'].'/{id}":{';
 | |
| 				foreach ($table['id_actions'] as $j=>$action) {
 | |
| 					if ($j>0) echo ',';
 | |
| 					echo '"'.$action['method'].'":{';
 | |
| 					echo '"tags":["'.$table['name'].'"],';
 | |
| 					echo '"summary":"'.ucfirst($action['name']).'",';
 | |
| 					echo '"parameters":[';
 | |
| 					echo '{';
 | |
| 					echo '"name":"id",';
 | |
| 					echo '"in":"path",';
 | |
| 					echo '"description":"Identifier for item.",';
 | |
| 					echo '"required":true,';
 | |
| 					echo '"type":"string"';
 | |
| 					echo '}';
 | |
| 					if ($action['name']=='update') {
 | |
| 						echo ',{';
 | |
| 						echo '"name":"item",';
 | |
| 						echo '"in":"body",';
 | |
| 						echo '"description":"Properties of item to update.",';
 | |
| 						echo '"required":false,';
 | |
| 						echo '"schema":{';
 | |
| 						echo '"type": "object",';
 | |
| 						echo '"properties": {';
 | |
| 						foreach (array_keys($action['fields']) as $k=>$field) {
 | |
| 							if ($k>0) echo ',';
 | |
| 							echo '"'.$field.'": {';
 | |
| 							echo '"type": "string"';
 | |
| 							if (isset($action['fields'][$field]->referenced)) {
 | |
| 								echo ',"x-referenced": '.json_encode($action['fields'][$field]->referenced);
 | |
| 							}
 | |
| 							if (isset($action['fields'][$field]->references)) {
 | |
| 								echo ',"x-references": '.json_encode($action['fields'][$field]->references);
 | |
| 							}
 | |
| 							if (isset($action['fields'][$field]->primaryKey)) {
 | |
| 								echo ',"x-primary-key": true';
 | |
| 							}
 | |
| 							echo '}';
 | |
| 						}
 | |
| 						echo '}'; //properties
 | |
| 						echo '}'; //schema
 | |
| 						echo '}';
 | |
| 					}
 | |
| 					echo '],';
 | |
| 					if ($action['name']=='read') {
 | |
| 						echo '"responses":{';
 | |
| 						echo '"200":{';
 | |
| 						echo '"description":"The requested item.",';
 | |
| 						echo '"schema":{';
 | |
| 						echo '"type": "object",';
 | |
| 						echo '"properties": {';
 | |
| 						foreach (array_keys($action['fields']) as $k=>$field) {
 | |
| 							if ($k>0) echo ',';
 | |
| 							echo '"'.$field.'": {';
 | |
| 							echo '"type": "string"';
 | |
| 							if (isset($action['fields'][$field]->referenced)) {
 | |
| 								echo ',"x-referenced": '.json_encode($action['fields'][$field]->referenced);
 | |
| 							}
 | |
| 							if (isset($action['fields'][$field]->references)) {
 | |
| 								echo ',"x-references": '.json_encode($action['fields'][$field]->references);
 | |
| 							}
 | |
| 							if (isset($action['fields'][$field]->primaryKey)) {
 | |
| 								echo ',"x-primary-key": true';
 | |
| 							}
 | |
| 							echo '}';
 | |
| 						}
 | |
| 						echo '}'; //properties
 | |
| 						echo '}'; //schema
 | |
| 						echo '}';
 | |
| 						echo '}';
 | |
| 					} else {
 | |
| 						echo '"responses":{';
 | |
| 						echo '"200":{';
 | |
| 						echo '"description":"Number of affected rows.",';
 | |
| 						echo '"schema":{';
 | |
| 						echo '"type":"integer"';
 | |
| 						echo '}';
 | |
| 						echo '}';
 | |
| 						echo '}';
 | |
| 					}
 | |
| 					echo '}';
 | |
| 				}
 | |
| 				echo '}';
 | |
| 			}
 | |
| 		}
 | |
| 		echo '}';
 | |
| 			echo '}';
 | |
| 	}
 | |
| 
 | |
| 	public function executeCommand() {
 | |
| 		if (isset($_SERVER['REQUEST_METHOD'])) {
 | |
| 			header('Access-Control-Allow-Origin: *');
 | |
| 		}
 | |
| 		if (!$this->settings['request']) {
 | |
| 			$this->swagger($this->settings);
 | |
| 		} else {
 | |
| 			$parameters = $this->getParameters($this->settings);
 | |
| 			switch($parameters['action']){
 | |
| 				case 'list': $this->listCommand($parameters); break;
 | |
| 				case 'read': $this->readCommand($parameters); break;
 | |
| 				case 'create': $this->createCommand($parameters); break;
 | |
| 				case 'update': $this->updateCommand($parameters); break;
 | |
| 				case 'delete': $this->deleteCommand($parameters); break;
 | |
| 				case 'headers': $this->headersCommand($parameters); break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // require 'auth.php'; // from the PHP-API-AUTH project, see: https://github.com/mevdschee/php-api-auth
 | |
| 
 | |
| // uncomment the lines below for token+session based authentication (see "login_token.html" + "login_token.php"):
 | |
| 
 | |
| // $auth = new PHP_API_AUTH(array(
 | |
| // 	'secret'=>'someVeryLongPassPhraseChangeMe',
 | |
| // ));
 | |
| // $auth->executeCommand();
 | |
| // if (empty($_SESSION['user'])) {
 | |
| //	header('HTTP/1.0 401 Unauthorized');
 | |
| //	exit(0);
 | |
| // }
 | |
| 
 | |
| // uncomment the lines below for form+session based authentication (see "login.html"):
 | |
| 
 | |
| // $auth = new PHP_API_AUTH(array(
 | |
| // 	'authenticator'=>function($user,$pass){ $_SESSION['user']=($user=='admin' && $pass=='admin'); }
 | |
| // ));
 | |
| // $auth->executeCommand();
 | |
| // if (empty($_SESSION['user'])) {
 | |
| //	header('HTTP/1.0 401 Unauthorized');
 | |
| //	exit(0);
 | |
| // }
 | |
| 
 | |
| // uncomment the lines below when running in stand-alone mode:
 | |
| 
 | |
| // $api = new PHP_CRUD_API(array(
 | |
| // 	'dbengine'=>'MySQL',
 | |
| // 	'hostname'=>'localhost',
 | |
| // 	'username'=>'',
 | |
| // 	'password'=>'',
 | |
| // 	'database'=>'',
 | |
| // 	'charset'=>'utf8'
 | |
| // ));
 | |
| // $api->executeCommand();
 | |
| 
 | |
| // For Microsoft SQL Server 2012 use:
 | |
| 
 | |
| // $api = new PHP_CRUD_API(array(
 | |
| // 	'dbengine'=>'SQLServer',
 | |
| // 	'hostname'=>'(local)',
 | |
| // 	'username'=>'',
 | |
| // 	'password'=>'',
 | |
| // 	'database'=>'xxx',
 | |
| // 	'charset'=>'UTF-8'
 | |
| // ));
 | |
| // $api->executeCommand();
 | |
| 
 | |
| // For PostgreSQL 9 use:
 | |
| 
 | |
| // $api = new PHP_CRUD_API(array(
 | |
| // 	'dbengine'=>'PostgreSQL',
 | |
| // 	'hostname'=>'localhost',
 | |
| // 	'username'=>'xxx',
 | |
| // 	'password'=>'xxx',
 | |
| // 	'database'=>'xxx',
 | |
| // 	'charset'=>'UTF8'
 | |
| // ));
 | |
| // $api->executeCommand();
 | |
| 
 | |
| // For SQLite 3 use:
 | |
| 
 | |
| // $api = new PHP_CRUD_API(array(
 | |
| // 	'dbengine'=>'SQLite',
 | |
| // 	'database'=>'data/blog.db',
 | |
| // ));
 | |
| // $api->executeCommand();
 | 
