/** * Compress web-application * @param boolean $debug Disable errors output * @param string $php_version PHP version to support */ public function compress($debug = false, $environment = 'prod', $php_version = PHP_VERSION) { // Set compressed project environment $this->environment = $environment; elapsed('Started web-application compression[' . $this->environment . ']'); s()->async(true); ini_set('memory_limit', '256M'); // Check output path if (!isset($this->output[0])) { return $this->log('Cannot compress web-application from [##] - No output path is specified', $this->input); } // Define rendering model depending on PHP version $php_version = isset($php_version[0]) ? $php_version : PHP_VERSION; if (version_compare($php_version, '5.3.0', '<')) { $this->view_mode = Core::RENDER_ARRAY; } // Add url base to path $this->output .= url()->base(); // Creating output project folder $result = \samson\core\File::mkdir($this->output); if ($result) { $this->log('Created output project folder [##]', $this->output); } else { if ($result == -1) { return $this->log('Compression failed! Cannot create output project folder [##]', $this->output); } } // Remove all trailing slashes $this->output = realpath($this->output) . '/'; $this->log('[##]## Compressing web-application[##] from [##] to [##]', $environment, $debug ? '[DEBUG]' : '', $php_version, $this->input, $this->output); // Add generic composer auto loader require $this->php['__before_all']['composer'] = "\n" . 'if(file_exists("vendor/autoload.php")) require "vendor/autoload.php";'; // Define global views collection $this->php[self::NS_GLOBAL][self::VIEWS] = "\n" . '$GLOBALS["__compressor_files"] = array();'; // If resourcer is loaded - copy css and js // Link $rr =& s()->module_stack['resourcer']; // Iterate all css and js resources $ignoreFolders = array(); foreach ($this->ignoredFolders as $folder) { $ignoreFolders[] = $this->output . $folder; } // Remove all old javascript and css \samson\core\File::clear($this->output, array('js', 'css'), $ignoreFolders); $moduleListArray = []; //$moduleListArray[Router::I_MAIN_PROJECT_TEMPLATE] = $this->system->module_stack; Event::fire(self::E_CREATE_MODULE_LIST, array(&$moduleListArray)); $resource = new Resource($this->fileManager); foreach ($moduleListArray as $template => $moduleList) { $resourceUrls = []; Event::fire(self::E_CREATE_RESOURCE_LIST, array(&$resourceUrls, $moduleList)); foreach ($resourceUrls as $type => $urls) { $file = $resource->compress($urls, $type, $this->output); $this->resourceUrlsList[$template][$type] = [DIRECTORY_SEPARATOR . $file]; } } // Iterate core ns resources collection foreach (s()->module_stack as $id => &$module) { // Work only with compressable modules if (is_a($module, ns_classname('CompressInterface', 'samsonframework\\core')) || isset($this->composerParameters['samsonphp_package_compressable']) && ($this->composerParameters['samsonphp_package_compressable'] = 1)) { $this->compress_module($module, $module->resourceMap); } // Change path to local modules if (is_a($module, '\\samson\\core\\VirtualModule')) { $module->path(''); } } /*foreach ($rr->cached['js'] as $jsCachedFile) { // Manage javascript resource $javascriptManager = new resource\JavaScript($this); $javascriptManager->compress(__SAMSON_CWD__ . $jsCachedFile, $this->output . basename($jsCachedFile)); } foreach ($rr->cached['css'] as $cssCachedFile) { // Manage CSS resource $cssManager = new resource\CSS($this, $rr); $cssManager->compress(__SAMSON_CWD__ . $cssCachedFile, $this->output . basename($cssCachedFile)); }*/ //} // Copy main project composer.json $composerPath = __SAMSON_CWD__ . 'composer.json'; if (file_exists($composerPath)) { // Read json file $composerJSON = (array) json_decode(file_get_contents($composerPath)); // Remove development dependencies unset($composerJSON['require-dev']); // Remove autoload section unset($composerJSON['autoload']); // Remove install/update scripts unset($composerJSON['scripts']); // Write modified composer.json file_put_contents($this->output . 'composer.json', json_encode($composerJSON)); } // Set errors output $this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . '\\samson\\core\\Error::$OUTPUT = ' . (!$debug ? 'false' : 'true') . ';'; // Create SamsonPHP core compressor $core = new \samsonphp\compressor\Core(s(), $environment, $this); // Add global base64 serialized core string $this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . '$GLOBALS["__CORE_SNAPSHOT"] = \'' . $core->compress() . '\';'; // Add all specified requires foreach ($this->require as $require) { $this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . 'require("' . $require . '");'; } // Add localization data $locale_str = array(); foreach (\samson\core\SamsonLocale::$locales as $locale) { if ($locale != '') { $locale_str[] = '\'' . $locale . '\''; } } // Add [setlocales] code $this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . 'setlocales( ' . implode(',', $locale_str) . ');'; // TODO: add generic handlers to modules to provide compressing logic for each module // TODO: add generic constants namespace to put all constants definition there - and put only defined constrat and redeclare them // TODO: WTF???? Thi must be local module logic // If this is remote web-app - collect local resources if (__SAMSON_REMOTE_APP) { // Gather all resources $path = __SAMSON_CWD__; $ls = array(); s()->resources($path, $ls); // If we have any resources if (isset($ls['resources'])) { $this->copy_path_resources($ls['resources'], __SAMSON_CWD__, ''); } } // If default locale is defined if (!defined('DEFAULT_LOCALE')) { define('DEFAULT_LOCALE', 'ru'); } // Add default system locale to them end of core definition $this->php['samson\\core'][self::VIEWS] = "\n" . 'define("DEFAULT_LOCALE", "' . DEFAULT_LOCALE . '");'; // Pointer to entry script code $entryScriptPath = __SAMSON_CWD__ . __SAMSON_PUBLIC_PATH . 'index.php'; $entryScript =& $this->php[self::NS_GLOBAL][$entryScriptPath]; // Collect all event system data $eventCompressor = new EventCompressor(); $eventCompressor->collect($entryScript); // Remove standard framework entry point from index.php - just preserve default controller if (preg_match('/start\\(\\s*(\'|\\")(?<default>[^\'\\"]+)/i', $entryScript, $matches)) { /* * Temporary solution to support compressed version, because other way localization does not work, * as chain is broken, first time URL object is created and URL is parsed only after start, so * CMS::afterCompress does not knows what is current locale and does not inject it to all material * queries. */ $this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . 'url();'; $this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . 's()->start(\'' . $matches['default'] . '\');'; } else { e('Default module definition not found - possible errors at compressed version'); } // Clear default entry point unset($this->php[self::NS_GLOBAL][$entryScriptPath]); // Set global namespace as last $global_ns = $this->php[self::NS_GLOBAL]; unset($this->php[self::NS_GLOBAL]); $this->php[self::NS_GLOBAL] = $global_ns; // Set view data to the end of global namespace $s = $this->php[self::NS_GLOBAL][self::VIEWS]; unset($this->php[self::NS_GLOBAL][self::VIEWS]); $this->php[self::NS_GLOBAL][self::VIEWS] = $s; // Load all OOP entities $classes = array(); // Соберем коллекцию загруженных интерфейсов их файлов по пространствам имен $this->classes_to_ns_files(get_declared_interfaces(), $classes); // Соберем коллекцию загруженных классов их файлов по пространствам имен $this->classes_to_ns_files(get_declared_classes(), $classes); // Fix OOP entities foreach ($this->php as $ns => &$files) { // If this namespace has been loaded if (isset($classes[$ns])) { // Fill namespace entities, make OOP entities correct order $files = array_merge($classes[$ns], $files); } } // Соберем весь PHP код в один файл $index_php = $this->code_array_to_str($this->php, $this->view_mode == 2); // Collect all event system data $eventCompressor->collect($index_php); // Transform event system in all project code if ($eventCompressor->transform($index_php, $index_php)) { //trace($eventCompressor->subscriptions, true); } // Remove url_base parsing and put current url base if (preg_match('/define\\(\'__SAMSON_BASE__\',\\s*([^;]+)/i', $index_php, $matches)) { $index_php = str_replace($matches[0], 'define(\'__SAMSON_BASE__\',\'' . __SAMSON_BASE__ . '\');', $index_php); } // Set global constant to specify supported PHP version if (preg_match('/define\\s*\\(\'__SAMSON_PHP_OLD[^;]+/', $index_php, $matches)) { $index_php = str_replace($matches[0], 'define(\'__SAMSON_PHP_OLD\',\'' . ($this->view_mode == 2) . '\');', $index_php); } $index_php = $this->removeBlankLines($index_php); $index_php = preg_replace('/(declare *\\( *strict_types *= *1 *\\) *;)/i', ' ', $index_php); // Запишем пусковой файл file_put_contents($this->output . 'index.php', '<?php ' . $index_php . "\n" . '?>'); // Minify PHP code if no debug is needed if (!$debug) { file_put_contents($this->output . 'index.php', php_strip_whitespace($this->output . 'index.php')); } elapsed('Site has been successfully compressed to ' . $this->output); }
/** * Create static HTML site version */ public function compress() { // If no output path specified if (!isset($this->output[0])) { $this->output = __SAMSON_PUBLIC_PATH . '/out/'; } // Create output directory and clear old HTML data if (\samson\core\File::mkdir($this->output)) { \samson\core\File::clear($this->output); } // Save original output path $o_output = $this->output; elapsed('Creating static HTML web-application from: ' . $this->input . ' to ' . $this->output); // Collection of existing generated views $views = '<h1>#<i>' . $_SERVER['HTTP_HOST'] . '</i> .html pages:</h1>'; $views .= 'Generated on ' . date('d M Y h:i'); // Path to new resources $cssPath = ''; $jsPath = ''; // Copy generated css & js resources to root folder if (class_exists('\\samson\\resourcer\\ResourceRouter')) { $rr = m('resourcer'); // Get resourcer CSS generated files $cssPath = $this->input . $rr->cached['css']; if (isset($cssPath)) { elapsed('Creating CSS resource file from:' . $cssPath); // Read CSS file $css = file_get_contents($cssPath); // Perform URL rewriting $css = preg_replace_callback('/url\\s*\\(\\s*(\'|\\")?([^\\)\\s\'\\"]+)(\'|\\")?\\s*\\)/i', array($this, 'srcReplaceCallback'), $css); //$css = preg_replace('url((.*?)=si', '\\www', $css); $css = str_replace('url("fonts/', 'url("www/fonts/', $css); $css = str_replace('url("img/', 'url("www/img/', $css); // Write new CSS file file_put_contents($this->output . 'style.css', $css); } // Get resourcer JS generated files $jsPath = $rr->cached['js']; if (isset($jsPath)) { elapsed('Creating JavaScript resource file from:' . $jsPath); $this->copy_resource($this->input . $jsPath, $this->output . 'index.js'); } } // Iterate all site supported locales foreach (\samson\core\SamsonLocale::$locales as $locale) { // Generate localized path to cached html pages $pages_path = $this->cache_path . locale_path($locale); // Set views locale description $views .= '<h2>Locale <i>' . ($locale == \samson\core\SamsonLocale::DEF ? 'default' : $locale) . '</i>:<h2>'; // Get original output path $this->output = $o_output; //создаем набор дескрипторов cURL $mh = curl_multi_init(); //$__modules = array('local', 'account', 'clients', 'dashboard', 'login', 'main', 'notification', 'project', 'sidebar', 'translators'); $system_methods = array('__construct', '__sleep', '__destruct', '__get', '__set', '__call', '__wakeup'); //handler // TODO: this is not should be rewritten to support new router // Perform generation of every controller foreach (s()->module_stack as $id => $ctrl) { $rmController = sizeof($ctrl->resourceMap->controllers) ? $ctrl->resourceMap->controllers : $ctrl->resourceMap->module; //trace($rmController, 1); $controller = array(); //trace($controller, true); if (isset($rmController[0]) && class_exists($rmController[0])) { if (!substr_count($rmController[1], 'vendor')) { $methods = get_class_methods($rmController[0]); foreach ($methods as $method) { if (!in_array($method, $system_methods)) { if ($method == '__handler') { $controller[] = '/' . $id; } elseif (substr_count($method, '__') && !substr_count($method, '__async')) { $new_method = str_replace('__', '', $method); $controller[] = '/' . $id . '/' . $new_method; } } } } } else { $controller = $rmController; } foreach ($controller as $cntrl) { if (strpos($cntrl, '.php')) { // generate controller URL $cntrl = '/' . locale_path('ru') . strtolower(basename($cntrl, '.php')); } elapsed('Generating HTML snapshot for: ' . $cntrl); // Create curl instance $ch = \curl_init('127.0.0.1' . $cntrl); // Set base request options \curl_setopt_array($ch, array(CURLOPT_VERBOSE => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array('Host: ' . $_SERVER['HTTP_HOST']))); // Add curl too multi request curl_multi_add_handle($mh, $ch); } } // TODO: Create function\module for this // Curl multi-request $active = null; do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active && $mrc == CURLM_OK) { if (curl_multi_select($mh) != -1) { do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } } curl_multi_close($mh); // Files array $files = array(); // Iterate generated pages foreach (\samson\core\File::dir($pages_path, 'html', null, $files, 0) as $f) { // Ignore default controller index page as it will be included in this controller if (strpos($f, '/index.html') != false) { continue; } // Read HTML file $html = file_get_contents($f); // If we have resourcer CSS resource if (isset($cssPath[0])) { //$html = str_replace(basename($cssPath, '.css'), 'style', $html); //$html = str_replace('/cache/resourcer/style.css', 'style.css', $html); $html = preg_replace("'<link type=\"text/css\"[^>]*?>'si", '<link type="text/css" rel="stylesheet" href="style.css">', $html); } // If we have resourcer JS resource if (isset($jsPath[0])) { $html = preg_replace("'<script[^>]*?></script>'si", '<script type="text/javascript" src="index.js"></script>', $html); } // Change path in all img SRC attributes if (preg_match_all('/< *img[^>]*src *= *["\']?([^"\']*)/i', $html, $matches)) { if (isset($matches['url'])) { foreach ($matches['url'] as $match) { trace($match . '-' . (__SAMSON_PUBLIC_PATH . ltrim($match, '/'))); $html = str_ireplace($match, __SAMSON_PUBLIC_PATH . ltrim($match, '/'), $html); } } } // Fix images inside modules $html = str_replace('<img src="img', '<img src="www/img', $html); // Remove relative links $html = str_ireplace('<base href="/">', '', $html); // Get just file name $view_path = ($locale != \samson\core\SamsonLocale::DEF ? $locale . '_' : '') . basename($f); // Save HTML file file_put_contents($this->output . $view_path, $html); // Create index.html record $views .= '<a href="' . $view_path . '">' . basename($f) . '</a><br>'; } } // Write index file file_put_contents($this->output . 'index.html', $views); // TODO: Add support to external-internal modules // Iterate other resources foreach (\samson\core\ResourceMap::get($this->input, false, array(__SAMSON_PUBLIC_PATH . 'out/'))->resources as $type => $files) { if (!in_array($type, $this->restricted)) { foreach ($files as $f) { $this->copy_resource($f, str_replace($this->input, $this->output, $f)); } } } $imagesArray = array('png', 'jpg', 'jpeg', 'gif'); foreach (s()->module_stack as $module) { if (isset($module->resourceMap->module[1]) && !substr_count($module->resourceMap->module[1], 'vendor')) { foreach ($imagesArray as $format) { if (isset($module->resourceMap->resources[$format]) && sizeof($module->resourceMap->resources[$format])) { foreach ($module->resourceMap->resources[$format] as $file) { $this->copy_resource($file, $this->output . 'www/img/' . basename($file)); } } } } } // Generate zip file name $zip_file = $o_output . 'www.zip'; elapsed('Creating ZIP file: ' . $zip_file); // Create zip archieve $zip = new \ZipArchive(); if ($zip->open($zip_file, \ZipArchive::CREATE) === true) { foreach (\samson\core\File::dir($this->output) as $file) { $zip->addFile($file, str_replace($this->output, '', $file)); } $zip->close(); } else { elapsed('Cannot create zip file'); } }