1: <?php
2:
3: namespace Alchemy\engine;
4: use Alchemy\core\query\IQuery;
5: use Alchemy\dialect\Compiler;
6: use Alchemy\util\Monad;
7: use PDO;
8:
9:
10: 11: 12:
13: class Engine implements IEngine {
14: protected $compiler;
15: protected $connector;
16: protected $echoQueries = false;
17: protected $pendingTransaction = false;
18:
19:
20: 21: 22: 23: 24: 25: 26:
27: public function __construct(Compiler $compiler, $dsn, $username = '', $password = '') {
28:
29: $this->connector = new PDO($dsn, $username, $password);
30: $this->connector->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
31:
32: $this->compiler = $compiler;
33: }
34:
35:
36: 37: 38: 39: 40:
41: public function beginTransaction() {
42: if (!$this->pendingTransaction) {
43: $this->connector->beginTransaction();
44: $this->pendingTransaction = true;
45: }
46: }
47:
48:
49: 50: 51:
52: public function commitTransaction() {
53: if ($this->pendingTransaction) {
54: $this->connector->commit();
55: $this->pendingTransaction = false;
56: }
57: }
58:
59:
60: 61: 62: 63: 64: 65:
66: protected function getBindParamType($obj) {
67: static $map = array(
68: 'boolean' => PDO::PARAM_BOOL,
69: 'integer' => PDO::PARAM_INT,
70: 'null' => PDO::PARAM_NULL);
71:
72: $type = $obj->getTag('expr.value');
73: return array_key_exists($type, $map) ? $map[$type] : PDO::PARAM_STR;
74: }
75:
76:
77: 78: 79: 80: 81:
82: protected function echoQuery($sql, $params) {
83: if (!$this->echoQueries) {
84: return;
85: }
86:
87: if (is_callable($this->echoQueries)) {
88: return $this->echoQueries($sql, $params);
89: }
90:
91: $sql = preg_replace_callback("/:([\w\d]+)/sui", function($match) use ($params) {
92: $v = $params[$match[1]];
93: if (is_null($v)) {
94: return 'NULL';
95: }
96:
97: if (is_numeric($v)) {
98: return $v;
99: }
100:
101: if (mb_strlen($v) > 13) {
102: $v = mb_substr($v, 0, 10) . '...';
103: }
104: return "'{$v}'";
105: }, $sql);
106:
107: echo $sql . "\n";
108: }
109:
110:
111: 112: 113: 114: 115: 116:
117: public function query(IQuery $query) {
118: $sql = $this->compiler->compile($query);
119: $sql = is_array($sql) ? $sql : array($sql);
120: $params = $query->parameters();
121:
122:
123:
124:
125:
126:
127:
128:
129: $result = null;
130: foreach ($sql as $q) {
131: $result = $this->execute($q, $params);
132: }
133:
134: return $result;
135: }
136:
137:
138: 139: 140: 141: 142: 143: 144:
145: public function execute($sql, $params = array()) {
146: try {
147: $statement = $this->connector->prepare($sql);
148: } catch (Exception $e) {
149: throw new Exception("Could not prepare query: " . $sql);
150: }
151:
152: $paramLog = array();
153: foreach ($params as $param) {
154: $type = $this->getBindParamType($param);
155: $alias = $this->compiler->alias($param);
156: $value = $param->getValue();
157: $paramLog[$alias] = $value;
158: $statement->bindValue($alias, $value, $type);
159: }
160:
161: $this->echoQuery($sql, $paramLog);
162:
163: $statement->execute();
164: return new ResultSet($this->connector, $statement);
165: }
166:
167:
168: 169: 170:
171: public function rollbackTransaction() {
172: if ($this->pendingTransaction) {
173: $this->connector->rollBack();
174: }
175: }
176:
177:
178: 179: 180: 181: 182:
183: public function setEcho($echoQueries) {
184: $this->echoQueries = $echoQueries;
185: }
186: }
187: