PageRenderTime 421ms CodeModel.GetById 109ms RepoModel.GetById 78ms app.codeStats 0ms

/classes/bors/object/simple.php

https://bitbucket.org/Balancer/bors-core
PHP | 413 lines | 296 code | 72 blank | 45 comment | 61 complexity | a766ca253e85748f39e353b993cfb732 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-3.0
  1. <?php
  2. /**
  3. Минимальный полноценный BORS-объект
  4. Всё необходимое для полноценной работы
  5. Без бэкенда.
  6. */
  7. require_once __DIR__.'/../../../inc/system.php';
  8. class bors_object_simple extends bors_object_empty
  9. {
  10. var $___id;
  11. var $attr = array();
  12. var $_prop_joins = array();
  13. var $defaults = array();
  14. function id() { return $this->___id; }
  15. function set_id($id) { return $this->___id = $id; }
  16. function __construct($id)
  17. {
  18. $this->set_id($this->initial_id = $id);
  19. }
  20. static function foo($id = NULL)
  21. {
  22. return bors_foo(get_called_class(), $id);
  23. }
  24. function get($name, $default = NULL)
  25. {
  26. return $this->__get_ex($name, $default);
  27. }
  28. function __get_ex($name, $default = NULL, $skip_methods = false, $skip_properties = false)
  29. {
  30. static $get_lock = array();
  31. $lock_name = $this->internal_uri().'.'.$name;
  32. // Начинается только с символов, т.к. дальше может быть xxxx()->yyy()
  33. if(!preg_match('/^\w+[\(\)\->\|]*/', $name))
  34. {
  35. bors_debug::syslog('error-get-property-name', "Incorrect property name '$name' for ".$this->debug_title());
  36. return NULL;
  37. }
  38. if(!empty($get_lock[$lock_name]))
  39. return NULL;
  40. $get_lock[$lock_name] = true;
  41. if(!preg_match('/^\w+$/', $name))
  42. {
  43. // Если оформлено как функциональный вызов
  44. // Хак: если это SQL-id вида 'id' => "CONCAT_WS('-', person_id, list_id)",
  45. if(preg_match('/^CONCAT/', $name))
  46. {
  47. unset($get_lock[$lock_name]);
  48. return NULL;
  49. }
  50. $result = NULL;
  51. if(preg_match('/^(.+)\|(\w+)$/', $name, $m))
  52. {
  53. $name = $m[1];
  54. $post_function = $m[2];
  55. }
  56. else
  57. $post_function = NULL;
  58. try
  59. {
  60. $result = $this;
  61. $ok = true;
  62. // Если сразу вызывать eval для цепочек $this->target()->titled_link(),
  63. // то вываливается неперехватываемое исключение, если target() вернёт NULL
  64. // Поэтому пробуем разобрать по цепочке.
  65. foreach(explode('->', $name) as $method)
  66. {
  67. if(preg_match('/(\w+)\(\)/', $method, $m))
  68. {
  69. // Если следом вызов метода от NULL, то облом и возвращаем умолчение.
  70. if(is_null($result))
  71. {
  72. unset($get_lock[$lock_name]);
  73. return $default;
  74. }
  75. $result = call_user_func(array($result, $m[1]));
  76. }
  77. else
  78. {
  79. // Попался не чисто функциональный вызов. Если так, то пробуем, всё же, eval();
  80. $ok = false;
  81. break;
  82. }
  83. }
  84. if(!$ok)
  85. eval("\$result = \$this->$name;");
  86. /** @var string|null $post_function */
  87. if($post_function)
  88. $result = $post_function($result);
  89. }
  90. catch(Exception $e)
  91. {
  92. $result = $default;
  93. }
  94. unset($get_lock[$lock_name]);
  95. return $result;
  96. }
  97. if(method_exists($this, $name) && !$skip_methods)
  98. {
  99. $value = NULL;
  100. try { $value = call_user_func([$this, $name]); }
  101. catch(Exception $e) { $value = $default; }
  102. unset($get_lock[$lock_name]);
  103. return $value;
  104. }
  105. // У атрибутов приоритет выше, так как они могут перекрывать data.
  106. // Смотри также в __call
  107. if(array_key_exists($name, $this->attr))
  108. {
  109. unset($get_lock[$lock_name]);
  110. return $this->attr[$name];
  111. }
  112. if(!empty($this->data) && array_key_exists($name, $this->data))
  113. {
  114. unset($get_lock[$lock_name]);
  115. return $this->data[$name];
  116. }
  117. if($name == 'this')
  118. {
  119. unset($get_lock[$lock_name]);
  120. return $this;
  121. }
  122. // Проверяем параметры присоединённых объектов
  123. if($prop_joins = @$this->_prop_joins)
  124. foreach($prop_joins as $x)
  125. if(array_key_exists($name, $x->data))
  126. {
  127. unset($get_lock[$lock_name]);
  128. return $this->attr[$name] = $x->data[$name];
  129. }
  130. // Проверяем автоматические объекты.
  131. if(method_exists($this, 'auto_objects'))
  132. {
  133. $auto_objs = $this->auto_objects();
  134. if(!empty($auto_objs[$name]))
  135. if(preg_match('/^(\w+)\((\w+)\)$/', $auto_objs[$name], $m))
  136. {
  137. try { $value = bors_load($m[1], $this->get($m[2])); }
  138. catch(Exception $e) { $value = NULL; }
  139. unset($get_lock[$lock_name]);
  140. return $this->attr[$name] = $value;
  141. }
  142. }
  143. // Автоматические целевые объекты (имя класса задаётся)
  144. if(method_exists($this, 'auto_targets'))
  145. {
  146. $auto_targs = $this->auto_targets();
  147. if(!empty($auto_targs[$name]))
  148. if(preg_match('/^(\w+)\((\w+)\)$/', $auto_targs[$name], $m))
  149. {
  150. unset($get_lock[$lock_name]);
  151. return $this->attr[$name] = bors_load($this->get($m[1]), $this->get($m[2]));
  152. }
  153. }
  154. // Проверяем одноимённые переменные (var $title = 'Files')
  155. if(property_exists($this, $name) && !$skip_properties)
  156. {
  157. unset($get_lock[$lock_name]);
  158. return $this->set_attr($name, $this->$name);
  159. }
  160. // Проверяем одноимённые переменные, требующие перекодирования (var $title_ec = 'Сообщения')
  161. $name_ec = "{$name}_ec";
  162. if(property_exists($this, $name_ec) && !$skip_properties)
  163. {
  164. unset($get_lock[$lock_name]);
  165. return $this->set_attr($name, ec($this->$name_ec));
  166. }
  167. // Ищем методы, перекрываемые переменным по умолчанию
  168. $m = "_{$name}_def";
  169. if(method_exists($this, $m) && !$skip_methods)
  170. {
  171. try { $value = $this->$m(); }
  172. catch(Exception $e) { $value = NULL; }
  173. unset($get_lock[$lock_name]);
  174. return $this->attr[$name] = $value;
  175. }
  176. // С этого места и ниже никаких потенциально рекурсивных вызовов быть не должно, так что разлочиваем.
  177. unset($get_lock[$lock_name]);
  178. if(array_key_exists($name, $this->defaults))
  179. return $this->defaults[$name];
  180. return $default;
  181. }
  182. function get_ne($name, $def = NULL)
  183. {
  184. if(is_array($name))
  185. {
  186. $def = popval($name, 'default');
  187. $name = popval($name, 'property');
  188. }
  189. $val = $this->get($name);
  190. if($val)
  191. return $val;
  192. if(is_array($def) && !empty($def['property']))
  193. return $this->get_ne($def);
  194. return $def;
  195. }
  196. function get_data($name, $default = NULL, $auto_set = false)
  197. {
  198. if(@array_key_exists($name, $this->data))
  199. return $this->data[$name];
  200. if(array_key_exists($name, $this->attr))
  201. return $this->attr[$name];
  202. // Проверяем автоматические объекты.
  203. if(method_exists($this, 'auto_objects'))
  204. {
  205. $auto_objs = $this->auto_objects();
  206. if(!empty($auto_objs[$name]))
  207. if(preg_match('/^(\w+)\((\w+)\)$/', $auto_objs[$name], $m))
  208. return $this->attr[$name] = bors_load($m[1], call_user_func([$this, $m[2]]));
  209. }
  210. // Автоматические целевые объекты (имя класса задаётся)
  211. if(method_exists($this, 'auto_targets'))
  212. {
  213. $auto_targs = $this->auto_targets();
  214. if(!empty($auto_targs[$name]))
  215. if(preg_match('/^(\w+)\((\w+)\)$/', $auto_targs[$name], $m))
  216. return $this->attr[$name] = bors_load(call_user_func([$this, $m[1]]), call_user_func([$this, $m[2]]));
  217. }
  218. // Проверяем одноимённые переменные (var $title = 'Files')
  219. if(property_exists($this, $name))
  220. return $this->set_attr($name, $this->$name);
  221. // Проверяем одноимённые переменные, требующие перекодирования (var $title_ec = 'Сообщения')
  222. $name_ec = "{$name}_ec";
  223. if(property_exists($this, $name_ec))
  224. return $this->set_attr($name, ec($this->$name_ec));
  225. return $auto_set ? $this->set_attr($name, $default) : $default;
  226. }
  227. function is_set($name, $skip_methods = false, $skip_properties = false)
  228. {
  229. if(method_exists($this, $name) && !$skip_methods)
  230. return true;
  231. if(array_key_exists($name, $this->data))
  232. return true;
  233. if(array_key_exists($name, $this->attr))
  234. return true;
  235. // Проверяем автоматические объекты.
  236. if(method_exists($this, 'auto_objects'))
  237. {
  238. $auto_objs = $this->auto_objects();
  239. if(($f = @$auto_objs[$name]))
  240. if(preg_match('/^(\w+)\((\w+)\)$/', $f, $m))
  241. return true;
  242. }
  243. // Автоматические целевые объекты (имя класса задаётся)
  244. if(method_exists($this, 'auto_targets'))
  245. {
  246. $auto_targs = $this->auto_targets();
  247. if(($f = @$auto_targs[$name]))
  248. if(preg_match('/^(\w+)\((\w+)\)$/', $f, $m))
  249. return true;
  250. }
  251. // Проверяем одноимённые переменные (var $title = 'Files')
  252. if(property_exists($this, $name) && !$skip_properties)
  253. return true;
  254. // Проверяем одноимённые переменные, требующие перекодирования (var $title_ec = 'Сообщения')
  255. $name_ec = "{$name}_ec";
  256. if(property_exists($this, $name_ec) && !$skip_properties)
  257. return true;
  258. return false;
  259. }
  260. function _body_engine_def() { return ''; }
  261. function _storage_engine_def() { return ''; }
  262. function is_loaded() { return true; }
  263. function internal_uri()
  264. {
  265. return get_class($this).'://'
  266. //TODO: instead serialize make method for recursive object id. Without using __toString() in configures with cacheng empty results in _title_def();
  267. .((is_numeric($this->id())||is_string($this->id())) ? $this->id() : serialize($this->id()));
  268. }
  269. function cache_clean() { }
  270. function auto_search_index() { return false; }
  271. function __toString()
  272. {
  273. try
  274. {
  275. return $this->class_name().'://'.$this->id();
  276. }
  277. catch(Exception $e)
  278. {
  279. }
  280. return '__toString exception in '.get_class($this);
  281. }
  282. function attr($attr, $def = NULL) { return array_key_exists($attr, $this->attr) ? $this->attr[$attr] : $def; }
  283. function set_attr($attr, $value) { return $this->attr[$attr] = $value; }
  284. function set_attrs($attrs)
  285. {
  286. $this->attr = array_merge($this->attr, $attrs);
  287. return $this;
  288. }
  289. function set_property($property, $value, $db_up = true)
  290. {
  291. $this->set($property, $value, $db_up);
  292. return $this;
  293. }
  294. function set_properties($properties, $db_up = true)
  295. {
  296. foreach($properties as $property => $value)
  297. $this->set($property, $value, $db_up);
  298. // var_dump($db_up, $properties, $this->body_data());
  299. return $this;
  300. }
  301. // private $__last_cache_key; // идентификатор последнего проверяемого по havec значения
  302. var $__last_cache_stack = array(); // Для реентерабельности
  303. function __havec($attr)
  304. {
  305. array_push($this->__last_cache_stack, $attr);
  306. return array_key_exists($attr, $this->attr);
  307. }
  308. function __lastc()
  309. {
  310. return $this->attr[array_pop($this->__last_cache_stack)];
  311. }
  312. function __setc($value)
  313. {
  314. return $this->attr[array_pop($this->__last_cache_stack)] = $value;
  315. }
  316. function __havefc()
  317. {
  318. $attr = '__cache_'.calling_function_name();
  319. array_push($this->__last_cache_stack, $attr);
  320. return array_key_exists($attr, $this->attr);
  321. }
  322. function load_attr($attr, $init)
  323. {
  324. if(array_key_exists($attr, $this->attr))
  325. return $this->attr[$attr];
  326. bors_debug::syslog('__need-to-rewrite-ugly-code', 'load-attr: '.$attr);
  327. return $this->attr[$attr] = $init;
  328. }
  329. public function __sleep() { return array_keys(get_object_vars($this)); }
  330. /* Лежит с целью отладки
  331. function __set($name, $value)
  332. {
  333. if($name == 'title' && $value == 'p1990_dealers_admin_files')
  334. {
  335. echo "Set $name to $value<br/>";
  336. echo bors_debug::trace();
  337. }
  338. return $this->$name = $value;
  339. }
  340. */
  341. }