/** * Конструктор * @param array $info Атрибуты объекта и свойства */ function __construct($info = []) { if ((!isset($info['parent']) || !isset($info['name'])) && isset($info['uri'])) { $names = F::splitRight('/', $info['uri'], true); $info['name'] = $names[1]; if (!isset($info['parent'])) { $info['parent'] = $names[0]; } } if (isset($info['properties'])) { foreach ($info['properties'] as $name => $child) { if (is_scalar($child)) { $child = ['value' => $child]; } $child['name'] = $name; $child['is_property'] = true; // if (!isset($child['is_default_logic'])) $child['is_default_logic'] = true; if (!isset($child['created']) && isset($info['created'])) { $child['created'] = $info['created']; } if (!isset($child['updated']) && isset($info['updated'])) { $child['updated'] = $info['updated']; } $child['is_exists'] = $info['is_exists']; if (isset($info['uri'])) { $child['uri'] = $info['uri'] . '/' . $name; } else { if (isset($this->_attributes['uri'])) { $child['uri'] = $this->_attributes['uri'] . '/' . $name; } } // Если у свойства нет прототипа, то определение прототипа через прототипы родителей if (!isset($child['proto']) && isset($info['proto'])) { $p = Data::read($info['proto']); do { $property = $p->{$name}; } while (!$property && ($p = $p->proto(null, true))); if ($property) { $child['proto'] = $property->uri(); } } $this->_children[$name] = Data::entity($child); $this->_children[$name]->_parent = $this; } unset($info['children']); } $this->_attributes = array_replace($this->_attributes, $info); }
function read($uri) { if (!($entity = Buffer::get_entity($uri))) { // Экземпляра объекта в буфере нет, проверяем массив его атрибутов в буфере $info = Buffer::get_info($uri); if (empty($info)) { try { if ($uri === '') { $file = DIR . 'project.info'; $dir = DIR; } else { $file = DIR . trim($uri, '/') . '/' . File::fileName($uri) . '.info'; $dir = DIR . trim($uri, '/'); } if (is_file($file)) { // Чтение информации об объекте $info = file_get_contents($file); $info = json_decode($info, true); $error = json_last_error(); if ($error != JSON_ERROR_NONE) { $info = []; } $info['uri'] = $uri; if (!empty($info['file'])) { $info['is_default_file'] = false; // $info['is_file'] = true; // $info['value'] = $info['file']; } if (!empty($info['logic'])) { $info['is_default_logic'] = false; } if (!isset($info['is_default_logic'])) { $info['is_default_logic'] = true; } $info['is_exists'] = true; // Инфо о свойствах в буфер $info = Buffer::set_info($info); } // else // if (is_dir($dir)){ // $info = [ // 'uri' => $uri, // 'is_exists' => true // ]; // $info = Buffer::set_info($info); // } } catch (\Exception $e) { return false; } } if (empty($info) && $uri) { // Поиск объекта в свойствах объекта list($parent_uri, $name) = F::splitRight('/', $uri); if ($parent = Data::read($parent_uri)) { $props = Buffer::get_props($parent_uri); $entity = $parent->child($name, isset($props[$name])); } else { $entity = false; } } else { // Создать экземпляр без свойств $entity = Data::entity($info); } } return $entity; }
/** * Сохранение сущности * @param Entity $entity * @throws Error */ function write($entity) { // @todo // Смена родителя/прототипа требует соответсвующие сдвиги в таблице отношений // При смене uri нужно обновить uri подчиненных $attr = $entity->attributes(); // Локальные id $attr['parent'] = isset($attr['parent']) ? $this->localId($attr['parent']) : 0; $attr['proto'] = isset($attr['proto']) ? $this->localId($attr['proto']) : 0; $attr['author'] = isset($attr['author']) ? $this->localId($attr['author']) : 0; //$this->localId(Auth::get_user()->uri()); // Подбор уникального имени, если указана необходимость в этом if ($entity->is_changed('uri') || !$entity->is_exists()) { $q = $this->db->prepare('SELECT 1 FROM {objects} WHERE parent=? AND `name`=? LIMIT 0,1'); $q->execute(array($attr['parent'], $attr['name'])); if ($q->fetch()) { //Выбор записи по шаблону имени с самым большим префиксом $q = $this->db->prepare('SELECT `name` FROM {objects} WHERE parent=? AND `name` REGEXP ? ORDER BY CAST((SUBSTRING_INDEX(`name`, "_", -1)+1) AS SIGNED) DESC LIMIT 0,1'); $q->execute(array($attr['parent'], '^' . $attr['name'] . '(_[0-9]+)?$')); if ($row = $q->fetch(DB::FETCH_ASSOC)) { preg_match('|^' . preg_quote($attr['name']) . '(?:_([0-9]+))?$|u', $row['name'], $match); $attr['name'] .= '_' . (isset($match[1]) ? $match[1] + 1 : 1); } } $entity->name($attr['name']); $attr['uri'] = $entity->uri(); } Buffer::set_entity($entity); // Локальный идентификатор объекта $attr['id'] = $this->localId($entity->uri($entity->is_exists()), true, $new_id); // Если смена файла, то удалить текущий файл if ($entity->is_changed('file')) { $current_file = $entity->changes('file'); if (is_string($current_file)) { File::delete($entity->dir(true) . $current_file); } } // Если привязан файл if (!$entity->is_default_file()) { if ($entity->is_file()) { $file_attache = $entity->file(); if (is_array($file_attache)) { // Загрузка файла if (!($attr['file'] = Data::save_file($entity, $file_attache))) { $attr['file'] = ''; } } else { $attr['file'] = basename($file_attache); } } else { // файла нет, но нужно отменить наследование файла $attr['file'] = ''; } } else { $attr['file'] = ''; } // Уникальность order // Если изменено на конкретное значение (не максимальное) if ($attr['order'] != Entity::MAX_ORDER && (!$entity->is_exists() || $entity->is_changed('order'))) { // Проверка, занят или нет новый order $q = $this->db->prepare('SELECT 1 FROM {objects} WHERE `parent`=? AND `order`=?'); $q->execute(array($attr['parent'], $attr['order'])); if ($q->fetch()) { // Сдвиг order существующих записей, чтоб освободить значение для новой $q = $this->db->prepare(' UPDATE {objects} SET `order` = `order`+1 WHERE `parent`=? AND `order`>=?'); $q->execute(array($attr['parent'], $attr['order'])); } unset($q); } else { // Новое максимальное значение для order, если объект новый или явно указано order=null if (!$entity->is_exists() || $attr['order'] == Entity::MAX_ORDER) { // Порядковое значение вычисляется от максимального существующего $q = $this->db->prepare('SELECT MAX(`order`) m FROM {objects} WHERE parent=?'); $q->execute(array($attr['parent'])); if ($row = $q->fetch(DB::FETCH_ASSOC)) { $attr['order'] = $row['m'] + 1; } unset($q); } } $this->db->beginTransaction(); // Если новое имя или родитель, то обновить свой URI и URI подчиненных if ($entity->is_changed('name') || $entity->is_changed('parent')) { if ($entity->is_exists()) { $current_uri = $entity->uri(true); $current_name = $entity->is_changed('name') ? $entity->changes('name') : $attr['name']; // Текущий URI $names = F::splitRight('/', $current_uri, true); $uri = (isset($names[0]) ? $names[0] . '/' : '') . $current_name; // Новый URI $names = F::splitRight('/', $attr['uri'], true); $uri_new = (isset($names[0]) ? $names[0] . '/' : '') . $attr['name']; $entity->change('uri', $uri_new); // @todo Обновление URI подчиенных в базе // нужно знать текущий уковень вложенности и локальный id // // $q = $this->db->prepare('UPDATE {ids}, {parents} SET {ids}.uri = CONCAT(?, SUBSTRING(uri, ?)) WHERE {parents}.parent_id = ? AND {parents}.object_id = {ids}.id AND {parents}.is_delete=0'); // $v = array($uri_new, mb_strlen($uri)+1, $attr['id']); // $q->execute($v); // // Обновление уровней вложенностей в objects // if (!empty($current) && $current['parent']!=$attr['parent']){ // $dl = $attr['parent_cnt'] - $current['parent_cnt']; // $q = $this->db->prepare('UPDATE {objects}, {parents} SET parent_cnt = parent_cnt + ? WHERE {parents}.parent_id = ? AND {parents}.object_id = {objects}.id AND {parents}.is_delete=0'); // $q->execute(array($dl, $attr['id'])); // // Обновление отношений // $this->makeParents($attr['id'], $attr['parent'], $dl, true); // } if (!empty($uri) && is_dir(DIR . $uri)) { // Переименование/перемещение папки объекта $dir = DIR . $uri_new; File::rename(DIR . $uri, $dir); if ($entity->is_changed('name')) { // Переименование файла класса File::rename($dir . '/' . $current_name . '.php', $dir . '/' . $attr['name'] . '.php'); // Переименование .info файла File::rename($dir . '/' . $current_name . '.info', $dir . '/' . $attr['name'] . '.info'); } } unset($q); } // Обновить URI подчиненных объектов не фиксируя изменения $entity->updateChildrenUri(); } // Если значение больше 255 if (mb_strlen($attr['value']) > 255) { $q = $this->db->prepare(' INSERT INTO {text} (`id`, `value`) VALUES (:id, :value) ON DUPLICATE KEY UPDATE `value` = :value '); $q->execute(array(':id' => $attr['id'], ':value' => $attr['value'])); $attr['value'] = mb_substr($attr['value'], 0, 255); $attr['is_text'] = 1; } else { $attr['is_text'] = 0; } // Запись $attr_names = array('id', 'parent', 'proto', 'author', 'order', 'name', 'value', 'file', 'is_text', 'is_draft', 'is_hidden', 'is_link', 'is_mandatory', 'is_property', 'is_relative', 'is_default_value', 'is_default_logic', 'created', 'updated'); $cnt = sizeof($attr_names); // Запись объекта (создание или обновление при наличии) // Объект идентифицируется по id if (!$entity->is_exists()) { $q = $this->db->prepare(' INSERT INTO {objects} (`' . implode('`, `', $attr_names) . '`) VALUES (' . str_repeat('?,', $cnt - 1) . '?) ON DUPLICATE KEY UPDATE `' . implode('`=?, `', $attr_names) . '`=? '); $i = 0; foreach ($attr_names as $name) { $value = $attr[$name]; $i++; $type = is_int($value) ? DB::PARAM_INT : (is_bool($value) ? DB::PARAM_BOOL : (is_null($value) ? DB::PARAM_NULL : DB::PARAM_STR)); $q->bindValue($i, $value, $type); $q->bindValue($i + $cnt, $value, $type); } $q->execute(); } else { $q = $this->db->prepare(' UPDATE {objects} SET `' . implode('`=?, `', $attr_names) . '`=? WHERE id = ? '); $i = 0; foreach ($attr_names as $name) { $value = $attr[$name]; $i++; $type = is_int($value) ? DB::PARAM_INT : (is_bool($value) ? DB::PARAM_BOOL : (is_null($value) ? DB::PARAM_NULL : DB::PARAM_STR)); $q->bindValue($i, $value, $type); } $q->bindValue(++$i, $attr['id']); $q->execute(); } $this->db->commit(); foreach ($entity->children() as $child) { Data::write($child); } return true; }
function work(Request $request) { $uri = ''; // $s = C::read(); // C::writeln(mb_detect_encoding($s,['utf-8','cp866'])); do { try { C::write(C::style($uri, [C::COLOR_GRAY_DARK]) . '> '); $commands = preg_split('/\\s/ui', trim(C::read())); $cmd_count = count($commands); if ($commands[0] == 'show') { $new_uri = !empty($commands[1]) ? $uri . '/' . trim($commands[1], ' \\/') : $uri; $obj = Data::read($new_uri); C::writeln(C::style(Trace::format($obj, false))); } else { if (mb_strlen($commands[0]) > 1 && ($commands[0][0] == '/' || mb_substr($commands[0], 0, 2) == '..')) { $new_uri = !empty($commands[0]) ? $uri . '/' . trim($commands[0], ' \\/') : $uri; $obj = Data::read($new_uri, false, true); if ($obj && $obj->is_exists()) { $uri = $obj->uri(); } else { C::writeln(C::style('Object does not exist', C::COLOR_RED)); } } else { if ($commands[0] == 'ls' || $commands[0] == 'children') { $list = Data::find(['from' => $uri, 'select' => empty($commands[1]) || $commands[1] != '-p' ? 'children' : 'properties', 'struct' => 'list', 'limit' => [0, 50]]); foreach ($list as $obj) { C::writeln(C::style($obj->name(), C::COLOR_BLUE)); } } else { if (preg_match('/^\\s*select(\\(|=)/ui', $commands[0])) { $cond = Data::normalizeCond($commands[0], false); if (!isset($cond['from'])) { $cond['from'] = $uri; } $result = Data::find($cond); C::writeln(C::style(Trace::format($result, false))); } else { if ($commands[0] == 'color') { if (!empty($commands[1])) { C::use_style($commands[1] == 'off' ? false : null); } else { C::use_style(true); } } else { if ($cmd_count > 1 && $commands[0] == 'attr') { $obj = Data::read($uri); $attr = $commands[1]; if ($obj instanceof Entity && $obj->is_exists() && $obj->is_attr($attr)) { if ($cmd_count > 2) { if ($commands[2] === "null") { $commands[2] = null; } else { if ($commands[2] === "false") { $commands[2] = false; } else { $commands[2] = trim(trim($commands[2], '"')); } } $obj->{$attr}($commands[2]); Data::write($obj); C::writeln(Trace::format($obj, false)); } else { C::writeln(C::style($obj->attr($attr)), C::COLOR_PURPLE); } } } else { if ($cmd_count > 2 && $commands[0] == 'new') { $new_uri = !empty($commands[1]) ? $uri . '/' . trim($commands[1], ' \\/') : $uri; list($parent, $name) = F::splitRight('/', $new_uri); if (!$parent) { $parent = ''; } $proto = $commands[2]; $obj = Data::create($proto, $parent, ['name' => $name]); $signs = array_flip($commands); if (isset($signs['-m'])) { $obj->is_mandatory(true); } if (isset($signs['-p'])) { $obj->is_property(true); } if (isset($signs['-d'])) { $obj->is_draft(true); } if (isset($signs['-h'])) { $obj->is_hidden(true); } if (isset($signs['-l'])) { $obj->is_link(true); } if (isset($signs['-r'])) { $obj->is_relative(true); } $obj->complete(); Data::write($obj); C::writeln(Trace::format($obj, false)); } else { if ($commands[0] == 'complete') { $obj = Data::read($uri); $signs = array_flip($commands); $only_mandatory = !isset($signs['-all']) && !isset($signs['-not-mandatory']); $only_property = !isset($signs['-all']) && !isset($signs['-not-property']); $obj->complete($only_mandatory, $only_property); Data::write($obj); C::writeln(Trace::format($obj, false)); } else { C::writeln(C::style('Unknown command', C::COLOR_RED)); } } } } } } } } } catch (Error $e) { C::writeln(C::style($e->getMessage(), C::COLOR_RED)); } catch (\Exception $e) { C::writeln(C::style((string) $e, C::COLOR_RED)); } Buffer::clear(); } while ($commands[0] !== 'exit'); C::writeln("\nby!"); // if (!empty($request['ARG'][2])) { // if ($request['ARG'][2] == 'read') { // $uri = empty($request['ARG'][3]) ? '' : '/' . trim($request['ARG'][3], '/'); // $obj = Data::read($uri); // C::writeln(C::style(Trace::style($obj, false))); // } else // if ($request['ARG'][2] == 'find') { // $cond = empty($request['ARG'][3]) ? '' : trim($request['ARG'][3], '/'); // $result = Data::find($cond); // C::write(C::style(Trace::style($result, false))); // } else // if ($request['ARG'][2] == 'edit') { // $object = Data::read(empty($request['ARG'][3]) ? '' : '/'.trim($request['ARG'][3], '/')); // $attr = empty($request['ARG'][4]) ? null : $request['ARG'][4]; // $value = empty($request['ARG'][5]) ? null : $request['ARG'][5]; // if ($object instanceof Entity && $object->is_exists() && $attr){ // $object->{$attr}($value); // Data::write($object); // C::write(C::style(Trace::style($object, false))); // } // }else // { //// phpinfo(); //// C::writeln(Trace::style($_ENV, false)); //// C::writeln(getenv('ANSICON')); // C::writeln( // C::style(json_encode($request['ARG']), [C::STYLE_BOLD, C::STYLE_UNDERLINE, C::COLOR_BLUE])); // //$a = C::read(); // //C::writeln($a); // //// fwrite(STDOUT, $colors->getColoredString("Testing Colors class, this is purple string on yellow background.", "purple", "yellow") . "\n"); //// $line = fgets(STDIN); //// if(trim($line) != 'yes'){ //// echo "ABORTING!\n"; //// exit; //// } //// echo "\n"; //// echo "Thank you, continuing...\n"; //// return true; // } // } }
/** * Создание виртуального диска в Windows, для увеличения лимита на длину пути к файлам * @param $dir * @param bool $mkdir * @return mixed|string */ static function makeVirtualDir($dir, $mkdir = true) { if (self::$IS_WIN && mb_strlen($dir) > 248) { $dir = preg_replace('/\\\\/u', '/', $dir); $vdir = mb_substr($dir, 0, 248); $vdir = F::splitRight('/', $vdir); if ($vdir[0]) { $vdir = $vdir[0]; if (!is_dir($vdir)) { if ($mkdir) { mkdir($vdir, 0775, true); } else { return $dir; } } $dir = self::VIRT_DISK . ':/' . mb_substr($dir, mb_strlen($vdir) + 1); system('subst ' . self::VIRT_DISK . ': ' . $vdir); } } return $dir; }