/** * Copy file from source location to destination location with * analyzing last file modification time, and copying only changed files * * @param string $src source file * @param string $dst destination file * @param null $handler * * @return bool */ public function copy_resource($src, $dst, $handler = null) { if (!file_exists($src)) { return e('Cannot copy file - Source file(##) does not exists', E_SAMSON_SNAPSHOT_ERROR, $src); } // Action to do $action = null; // Get source file timestamp $source_ts = filemtime($src); // If destination file does not exists if (!file_exists($dst)) { $action = 'Creating'; } else { if (abs($source_ts - filemtime($dst)) > 125) { $action = 'Updating'; } } // If we know what to do if (isset($action)) { // Create folder structure if necessary $dir_path = pathname($dst); if (!file_exists($dir_path)) { elapsed(' -- Creating folder structure ' . $dir_path . ' from ' . $src); \samson\core\File::mkdir($dir_path); } // If file handler specified if (is_callable($handler)) { call_user_func($handler, $src, $dst, $action); } else { copy($src, $dst); } elapsed(' -- ' . $action . ' file ' . $dst . ' from ' . $src . '(Difference ' . date('H:i:s', abs($source_ts - filemtime($dst))) . ')'); // Touch source file with copied file touch($src); } }
/** * Выполнить рекурсивное "собирание" файла * * @param string $path Абсолютный путь к файлу сайта * * @param null $module * @param array $code * @param string $namespace * * @return string */ public function compress_php($path, $module = NULL, &$code = array(), $namespace = self::NS_GLOBAL) { // TODO: Довести до ума разпознование require - убрать точку с зяпятоц которая остается // TODO: Убрать пустые линии // TODO: Анализатор использования функция и переменных?? //trace(' + Вошли в функцию:'.$path.'('.$namespace.')'); $_path = $path; $path = normalizepath(realpath($path)); // Если мы уже подключили данный файл или он не существует if (isset($this->files[$path])) { return $this->log(' ! Файл: [##], already compressed', $path); } else { if (!is_file($path)) { return $this->log(' ! Файл: [##], не существует', $_path); } else { if (strpos($path, 'vendor/autoload.php') !== false) { return $this->log(' Ignoring composer autoloader [##]', $path); } else { if (in_array(basename($path), $this->ignoredFiles)) { return $this->log(' Ignoring file[##] by configuration', $path); } } } } $this->log(' -- Compressing file [##]', $path); // Load file require_once $path; //trace('Чтение файла: '.$path ); // Сохраним файл $this->files[$path] = $path; // Относительный путь к файлу if (isset($rel_path)) { $this->files[$rel_path] = $path; } // Прочитаем php файл $fileStr = file_get_contents($path); // Если в файле нет namespace - считаем его глобальным if (strpos($fileStr, 'namespace') === false) { //$file_dir = ''; // Вырежим путь к файлу //$file_dir = (pathinfo( $path, PATHINFO_DIRNAME ) == '.' ? '' : pathinfo( $path, PATHINFO_DIRNAME ).'/'); // Сюда соберем код программы $main_code = ''; } $main_code = "\n" . '// Модуль: ' . m($module)->id() . ', файл: ' . $path . "\n"; // Создадим уникальную коллекцию алиасов для NS if (!isset($code[$namespace]['uses'])) { $code[$namespace]['uses'] = array(); } // Установим ссылку на коллекцию алиасов $uses =& $code[$namespace]['uses']; // Local file uses collection $file_uses = array(); // Получим константы документа $consts = get_defined_constants(); // Маркеры для отрезания специальных блоков которые не нужны в PRODUCTION $rmarker_st = '\\/\\/\\[PHPCOMPRESSOR\\(remove\\,start\\)\\]'; $rmarker_en = '\\/\\/\\[PHPCOMPRESSOR\\(remove\\,end\\)\\]'; // Найдем все "ненужные" блоки кода и уберем их $fileStr = preg_replace('/' . $rmarker_st . '.*?' . $rmarker_en . '/uis', '', $fileStr); $className = ''; $classConstList = array(); //TODO: Fix to normal external dependency with ResourceRouter $fileStr = preg_replace_callback('/(\\\\samson\\\\resourcer\\\\)?ResourceRouter::url\\((\'|\\")(?<path>[^,)]+)(\'|\\")(,(?<module>[^)]+))?\\);/i', array($this, 'rewriteResourceRouter'), $fileStr); /** @var bool $classStared Flag for matching trait uses */ $classStared = false; // Разберем код программы $tokens = token_get_all($fileStr); for ($i = 0; $i < sizeof($tokens); $i++) { // Получим следующий жетон из кода программы $token = $tokens[$i]; // Если просто строка if (is_string($token)) { $main_code .= $token; } else { // token array list($id, $text) = $token; // Перебирем тип комманды switch ($id) { case T_COMMENT: // Пропускаем все комментарии // Пропускаем все комментарии case T_DOC_COMMENT: case T_CLOSE_TAG: // Начало,конец файла // Начало,конец файла case T_OPEN_TAG: break; case T_WHITESPACE: $main_code .= $text; /*$main_code .= ' ';*/ break; // Обработаем алиасы // Обработаем алиасы case T_USE: $_use = ''; // Переберем все что иде после комманды алиаса for ($j = $i + 1; $j < sizeof($tokens); $j++) { // Получим идентификатор метки и текстовое представление $id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; $text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; //trace('"'.$id.'" - "'.$text.'"'); // Если use используется в функции if ($id == '(') { $j--; break; } // Если это закрывающая скобка - прекратим собирание пути к файлу if ($id == ';') { break; } // Все пробелы игнорирую if ($id == T_WHITESPACE) { continue; } // Если у метки есть текстовое представление if (isset($text)) { // Если єто константа if (isset($consts[$text])) { $_use .= $consts[$text]; } else { $_use .= $text; } } } // Если это не use в inline функции - добавим алиас в коллекцию // для данного ns с проверкой на уникальность if ($id !== '(') { // If this tait use if ($classStared) { // Consider rewriting trait usage fully qualified name //TODO: Not fully qualified trait name adds slash before $_use = strpos($_use, '\\') === false ? '\\' . $namespace . '\\' . $_use : $_use; // TODO: Import trait code if (!trait_exists($_use)) { throw new \Exception('Trait "' . $_use . '" does not exists in "' . $path . '"'); } else { $main_code .= ' use ' . $_use . ';'; } } else { // Преведем все use к одному виду if ($_use[0] !== '\\') { $_use = '\\' . $_use; } // Add local file uses $file_uses[] = $_use; // TODO: Вывести замечание что бы код везде был одинаковый if (!in_array($_use, $uses)) { $uses[] = $_use; } } } else { $main_code .= ' use '; } // Сместим указатель чтения файла $i = $j; break; case T_NAMESPACE: // Определим временное пространство имен $_namespace = ''; // Переберем все что иде после комманды подключения файла for ($j = $i + 1; $j < sizeof($tokens); $j++) { // Получим идентификатор метки и текстовое представление $id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; $text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; //trace('"'.$id.'" - "'.$text.'"'); // Если это закрывающая скобка - прекратим собирание пути к файлу if ($id == ')' || $id == ';' || $id == '{') { break; } // Все пробелы игнорирую if ($id == T_WHITESPACE) { continue; } // Если у метки есть текстовое представление if (isset($text)) { // Если єто константа if (isset($consts[$text])) { $_namespace .= $consts[$text]; } else { $_namespace .= $text; } } } // Если найденный NS отличается от текущего - установим переход к новому NS if ($namespace !== $_namespace) { // Сохраним новый как текущий $namespace = strtolower($_namespace); //trace(' #'.$i.' -> Изменили NS с '.$namespace.' на '.$_namespace); // Если мы еще не создали данный NS if (!isset($code[$namespace])) { $code[$namespace] = array(); } // Создадим уникальную коллекцию алиасов для NS if (!isset($code[$namespace]['uses'])) { $code[$namespace]['uses'] = array(); } // Установим ссылку на коллекцию алиасов $uses =& $code[$namespace]['uses']; } // Сместим указатель чтения файла $i = $j; break; // Выделяем код подключаемых файлов // Выделяем код подключаемых файлов case T_REQUIRE: case T_REQUIRE_ONCE: //case T_INCLUDE : //case T_INCLUDE : case T_INCLUDE_ONCE: // Получим путь к подключаемому файлу $file_path = ''; // Переберем все что иде после комманды подключения файла for ($j = $i + 1; $j < sizeof($tokens); $j++) { // Получим идентификатор метки и текстовое представление $id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; $text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; //trace('"'.$id.'" - "'.$text.'"'); // Если это закрывающая скобка - прекратим собирание пути к файлу if ($id == ';') { break; } // Все пробелы игнорирую if ($id == T_WHITESPACE) { continue; } // Если у метки есть текстовое представление if (isset($text)) { // Если єто константа if (isset($consts[$text])) { $file_path .= $consts[$text]; } else { $file_path .= $text; } } } // Если указан путь к файлу if (isset($file_path[1])) { // Уберем ковычки $file_path = str_replace(array("'", '"'), array('', ''), $file_path); // Если это не абсолютный путь - попробуем относительный if (!file_exists($file_path)) { $file_path = pathname($path) . $file_path; } // Если файл найден - получим его содержимое if (file_exists($file_path)) { //trace('Углубляемся в файл:'.$file_path.'('.$namespace.')'); // Углубимся в рекурсию $this->compress_php($file_path, $module, $code, $namespace); // Измением позицию маркера чтения файла $i = $j + 1; } } else { $main_code .= $text; } break; case T_INTERFACE: case T_CLASS: $classStared = true; $main_code .= $text; for ($j = $i + 1; $j < sizeof($tokens); $j++) { // Get id and text of token $id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; $text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; // Ignore all whitespace if ($id == T_WHITESPACE) { continue; } if (isset($text)) { $className = $text; break; } } break; case T_CONST: $main_code .= $text; $classConst = array(); $nameFlag = 'name'; for ($j = $i + 1; $j < sizeof($tokens); $j++) { // Get id and text of token $id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; $text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; if ($id == ';') { break; } // Ignore all whitespace if ($id == T_WHITESPACE) { continue; } if ($id == '=') { $nameFlag = 'value'; continue; } if (isset($text)) { // Is it defined constant if (isset($consts[$text])) { $classConst[$nameFlag] = $consts[$text]; } else { $classConst[$nameFlag] = $text; } } } $classConstList[$classConst['name']] = $classConst['value']; break; // Собираем основной код программы // Собираем основной код программы default: $main_code .= $text; break; } } } // Replace all class shortcut usage with full name if (count($file_uses)) { $main_code = $this->removeUSEStatement($main_code, $file_uses); } $matches = array(); if ($className == 'Module') { $temp = ''; } if (preg_match_all('/(?<start>[(=+-\\/*%., \\n\\t])(?<class>[\\\\a-zA-Z_]+)::(?<name>[a-zA-Z_]+)(?<end>[):;=+-\\/*%., \\n\\t])/i', $main_code, $matches)) { for ($i = 0; $i < sizeof($matches['name']); $i++) { $matchClass = $matches['class'][$i]; // If this is self - use current file class if ($matches['class'][$i] === 'self') { $constantName = $namespace . '\\' . $className; } elseif ($matches['class'][$i] == $className) { // If this is current class add namespace $constantName = $namespace . '\\' . $className; } elseif ($matches['class'][$i] === 'parent') { continue; } elseif ($matches['class'][$i] === 'static') { continue; } else { $constantName = $matches['class'][$i]; } // If constant has no namespace - use current if (strpos($constantName, '\\') === false) { $constantName = $namespace . '\\' . $constantName; } // Add constant name $constantName .= '::' . $matches['name'][$i]; $replaceName = $matches['start'][$i] . $matchClass . '::' . $matches['name'][$i] . $matches['end'][$i]; // Check if we have this constant defined if (defined($constantName)) { // Get constant value $value = constant($constantName); // Fix slashes, add quotes for string $value = is_string($value) ? str_replace('\\', '\\\\\\\\', "'" . $value . "'") : $value; $replacer = str_replace('\\', '\\\\', $replaceName); $replacer = str_replace(array(')', '('), array('\\)', '\\('), $replacer); // Replace constant call in the code $main_code = preg_replace('/' . $replacer . '/i', $matches['start'][$i] . $value . $matches['end'][$i], $main_code); } } } // Запишем в коллекцию кода полученный код $code[$namespace][$path] = $main_code; return $main_code; }
/** * Reccurent PHP code parser * * @param string $path Abcolute path to php file * @param Module $module Pointer to file owning module object * @param array $code Collection where we need to gather parsed PHP code * @param string $namespace Module namespace * * @return array */ protected function parse($path, $module = NULL, &$code = array(), $namespace = self::NS_GLOBAL) { $_path = $path; $path = normalizepath(realpath($path)); // Если мы уже подключили данный файл или он не существует if (isset($this->files[$path])) { return $this->log(' ! Файл: [##], already compressed', $path); } else { if (!is_file($path)) { return $this->log(' ! Файл: [##], не существует', $_path); } else { if (strpos($path, 'vendor/autoload.php') !== false) { return $this->log(' Ignoring composer autoloader [##]', $path); } else { if (in_array(basename($path), $this->ignoredFiles)) { return $this->log(' Ignoring file[##] by configuration', $path); } } } } $this->log(' - Parsing file [##]', $path); // Load file once, if it's not have been loaded before require_once $path; // Сохраним файл $this->files[$path] = $path; // Прочитаем php файл $fileStr = file_get_contents($path); $main_code = "\n" . '// Модуль: ' . m($module)->id() . ', файл: ' . $path . "\n"; // Создадим уникальную коллекцию алиасов для NS if (!isset($code[$namespace]['uses'])) { $code[$namespace]['uses'] = array(); } // Установим ссылку на коллекцию алиасов $uses =& $code[$namespace]['uses']; // Local file uses collection $file_uses = array(); // Получим константы документа $consts = get_defined_constants(); // Маркеры для отрезания специальных блоков которые не нужны в PRODUCTION $rmarker_st = '\\/\\/\\[PHPCOMPRESSOR\\(remove\\,start\\)\\]'; $rmarker_en = '\\/\\/\\[PHPCOMPRESSOR\\(remove\\,end\\)\\]'; // Найдем все "ненужные" блоки кода и уберем их $fileStr = preg_replace('/' . $rmarker_st . '.*?' . $rmarker_en . '/uis', '', $fileStr); // Разберем код программы $tokens = token_get_all($fileStr); for ($i = 0; $i < sizeof($tokens); $i++) { // Получим следующий жетон из кода программы $token = $tokens[$i]; // Если просто строка if (is_string($token)) { $main_code .= $token; } else { // token array list($id, $text) = $token; // Перебирем тип комманды switch ($id) { case T_COMMENT: // Пропускаем все комментарии // Пропускаем все комментарии case T_DOC_COMMENT: case T_CLOSE_TAG: // Начало,конец файла // Начало,конец файла case T_OPEN_TAG: break; case T_WHITESPACE: $main_code .= $text; /*$main_code .= ' ';*/ break; // Обработаем алиасы // Обработаем алиасы case T_USE: $_use = ''; // Переберем все что иде после комманды алиаса for ($j = $i + 1; $j < sizeof($tokens); $j++) { // Получим идентификатор метки и текстовое представление $id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; $text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; //trace('"'.$id.'" - "'.$text.'"'); // Если use используется в функции if ($id == '(') { $j--; break; } // Если это закрывающая скобка - прекратим собирание пути к файлу if ($id == ';') { break; } // Все пробелы игнорирую if ($id == T_WHITESPACE) { continue; } // Если у метки есть текстовое представление if (isset($text)) { // Если єто константа if (isset($consts[$text])) { $_use .= $consts[$text]; } else { $_use .= $text; } } } // Если это не use в inline функции - добавим алиас в коллекцию // для данного ns с проверкой на уникальность if ($id !== '(') { // Нижний регистр //$_use = strtolower($_use); // Преведем все use к одному виду if ($_use[0] !== '\\') { $_use = '\\' . $_use; } // Add local file uses $file_uses[] = $_use; // TODO: Вывести замечание что бы код везде был одинаковый if (!in_array($_use, $uses)) { $uses[] = $_use; } } else { $main_code .= ' use '; } // Сместим указатель чтения файла $i = $j; break; case T_NAMESPACE: // Определим временное пространство имен $_namespace = ''; // Переберем все что иде после комманды подключения файла for ($j = $i + 1; $j < sizeof($tokens); $j++) { // Получим идентификатор метки и текстовое представление $id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; $text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; //trace('"'.$id.'" - "'.$text.'"'); // Если это закрывающая скобка - прекратим собирание пути к файлу if ($id == ')' || $id == ';' || $id == '{') { break; } // Все пробелы игнорирую if ($id == T_WHITESPACE) { continue; } // Если у метки есть текстовое представление if (isset($text)) { // Если єто константа if (isset($consts[$text])) { $_namespace .= $consts[$text]; } else { $_namespace .= $text; } } } // Если найденный NS отличается от текущего - установим переход к новому NS if ($namespace !== $_namespace) { // Сохраним новый как текущий $namespace = strtolower($_namespace); //trace(' #'.$i.' -> Изменили NS с '.$namespace.' на '.$_namespace); // Если мы еще не создали данный NS if (!isset($code[$namespace])) { $code[$namespace] = array(); } // Создадим уникальную коллекцию алиасов для NS if (!isset($code[$namespace]['uses'])) { $code[$namespace]['uses'] = array(); } // Установим ссылку на коллекцию алиасов $uses =& $code[$namespace]['uses']; } // Сместим указатель чтения файла $i = $j; break; // Выделяем код подключаемых файлов // Выделяем код подключаемых файлов case T_REQUIRE: case T_REQUIRE_ONCE: //case T_INCLUDE : //case T_INCLUDE : case T_INCLUDE_ONCE: // Получим путь к подключаемому файлу $file_path = ''; // Переберем все что иде после комманды подключения файла for ($j = $i + 1; $j < sizeof($tokens); $j++) { // Получим идентификатор метки и текстовое представление $id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; $text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; //trace('"'.$id.'" - "'.$text.'"'); // Если это закрывающая скобка - прекратим собирание пути к файлу if ($id == ';') { break; } // Все пробелы игнорирую if ($id == T_WHITESPACE) { continue; } // Если у метки есть текстовое представление if (isset($text)) { // Если єто константа if (isset($consts[$text])) { $file_path .= $consts[$text]; } else { $file_path .= $text; } } } // Если указан путь к файлу if (isset($file_path[1])) { // Уберем ковычки $file_path = str_replace(array("'", '"'), array('', ''), $file_path); // Если это не абсолютный путь - попробуем относительный if (!file_exists($file_path)) { $file_path = pathname($path) . $file_path; } // Если файл найден - получим его содержимое if (file_exists($file_path)) { //trace('Углубляемся в файл:'.$file_path.'('.$namespace.')'); // Углубимся в рекурсию $this->compress_php($file_path, $module, $code, $namespace); // Измением позицию маркера чтения файла $i = $j + 1; } } else { $main_code .= $text; } break; // Собираем основной код программы // Собираем основной код программы default: $main_code .= $text; break; } } } //trace(' - Вышли из функции:'.$path.'('.$namespace.')'); //trace(''); // Replace all class shortcut usage with full name if (sizeof($file_uses)) { $main_code = $this->removeUSEStatement($main_code, $file_uses); } // Запишем в коллекцию кода полученный код $code[$namespace][$path] = $main_code; return $main_code; }
/** * Generate ORM classes * @param string $force Force class generation */ public function generate($force = false, $cachePath = '') { // Processed permanent table relations $db_relations = array(); // Get all virtual tables structure data $db_mapper = array(); // Получим информацию о всех таблицах из БД $rows = $this->fetch('SELECT `TABLES`.`TABLE_NAME` as `TABLE_NAME`, `COLUMNS`.`COLUMN_NAME` as `Field`, `COLUMNS`.`DATA_TYPE` as `Type`, `COLUMNS`.`IS_NULLABLE` as `Null`, `COLUMNS`.`COLUMN_KEY` as `Key`, `COLUMNS`.`COLUMN_DEFAULT` as `Default`, `COLUMNS`.`EXTRA` as `Extra` FROM `information_schema`.`TABLES` as `TABLES` LEFT JOIN `information_schema`.`COLUMNS` as `COLUMNS` ON `TABLES`.`TABLE_NAME`=`COLUMNS`.`TABLE_NAME` WHERE `TABLES`.`TABLE_SCHEMA`="' . $this->database . '" AND `COLUMNS`.`TABLE_SCHEMA`="' . $this->database . '"'); foreach ($rows as $row) { // Получим имя таблицы $table_name = $row['TABLE_NAME']; // Создадим коллекцию для описания структуры таблицы if (!isset(self::$tables[$table_name])) { self::$tables[$table_name] = array(); } // Удалим имя таблицы из масива unset($row['TABLE_NAME']); // Запишем описание каждой колонки таблиц в специальный массив self::$tables[$table_name][] = $row; } $bstr = md5(serialize(self::$tables)); //TODO: check if virtual table has not changed and add it to hash // Создадим имя файла содержащего пути к модулям $md5_file = $cachePath . 'metadata/classes_' . $bstr . '.php'; $md5_file_func = $cachePath . 'metadata/func_' . $bstr . '.php'; // Если еще не создан отпечаток базы данных - создадим его if (!file_exists($md5_file) || $force) { // Get directory path $dir = pathname($md5_file); // Create folder if (!file_exists($dir)) { mkdir($dir, 0777, true); } else { File::clear($dir); } // Удалим все файлы с расширением map //foreach ( \samson\core\File::dir( getcwd(), 'dbs' ) as $file ) unlink( $file ); // Если еще не создан отпечаток базы данных - создадим его // Сохраним классы БД $db_classes = 'namespace samson\\activerecord;'; $db_func = ''; // Создадим классы foreach ($db_mapper as $table_name => $table_data) { $file_full = $this->classes($table_data, $table_name, $virtualTable->table, $db_relations); $db_classes .= $file_full[0]; $db_func .= $file_full[1]; } // Создадим классы foreach (self::$tables as $table_name => $table_data) { $file_full = $this->classes(self::$tables[$table_name], $table_name, $table_name, $db_relations); $db_classes .= $file_full[0]; $db_func .= $file_full[1]; } // Запишем файл для IDE в корень проекта file_put_contents($md5_file, '<?php ' . $db_classes . '?>'); file_put_contents($md5_file_func, '<?php ' . $db_func . '?>'); // Подключим наш ХУК для АктивРекорда!!!!! eval($db_classes); eval($db_func); } else { include $md5_file; include $md5_file_func; } //elapsed('end'); }