function sample($msg, $type = PROFILE_RUNNING_TOTAL) { if ($type == PROFILE_RUNNING_TOTAL) { $this->_records[] = array($msg, precision_timer("stop", $this->_starttime), precision_timer()); } else { $prev = end($this->_records); $this->_records[] = array($msg, precision_timer("stop", $prev[2]), precision_timer()); } }
/** * Takes an SQL string and executes it. This function will apply query * caching if it is a read operation and if query caching is set. Symphony * will convert the `tbl_` prefix of tables to be the one set during installation. * A type parameter is provided to specify whether `$this->_lastResult` will be an array * of objects or an array of associative arrays. The default is objects. This * function will return boolean, but set `$this->_lastResult` to the result. * * @uses PostQueryExecution * @param string $query * The full SQL query to execute. * @param string $type * Whether to return the result as objects or associative array. Defaults * to OBJECT which will return objects. The other option is ASSOC. If $type * is not either of these, it will return objects. * @throws DatabaseException * @return boolean * True if the query executed without errors, false otherwise */ public function query($query, $type = "OBJECT") { if (empty($query) || self::isConnected() === false) { return false; } $start = precision_timer(); $query = trim($query); $query_type = $this->determineQueryType($query); $query_hash = md5($query . $start); if (self::$_connection['tbl_prefix'] !== 'tbl_') { $query = preg_replace('/tbl_(\\S+?)([\\s\\.,]|$)/', self::$_connection['tbl_prefix'] . '\\1\\2', $query); } // TYPE is deprecated since MySQL 4.0.18, ENGINE is preferred if ($query_type == self::__WRITE_OPERATION__) { $query = preg_replace('/TYPE=(MyISAM|InnoDB)/i', 'ENGINE=$1', $query); } elseif ($query_type == self::__READ_OPERATION__ && !preg_match('/^SELECT\\s+SQL(_NO)?_CACHE/i', $query)) { if ($this->isCachingEnabled()) { $query = preg_replace('/^SELECT\\s+/i', 'SELECT SQL_CACHE ', $query); } else { $query = preg_replace('/^SELECT\\s+/i', 'SELECT SQL_NO_CACHE ', $query); } } $this->flush(); $this->_lastQuery = $query; $this->_lastQueryHash = $query_hash; $this->_result = mysqli_query(self::$_connection['id'], $query); $this->_lastInsertID = mysqli_insert_id(self::$_connection['id']); self::$_query_count++; if (mysqli_error(self::$_connection['id'])) { $this->__error(); } elseif ($this->_result instanceof mysqli_result) { if ($type == "ASSOC") { while ($row = mysqli_fetch_assoc($this->_result)) { $this->_lastResult[] = $row; } } else { while ($row = mysqli_fetch_object($this->_result)) { $this->_lastResult[] = $row; } } mysqli_free_result($this->_result); } $stop = precision_timer('stop', $start); /** * After a query has successfully executed, that is it was considered * valid SQL, this delegate will provide the query, the query_hash and * the execution time of the query. * * Note that this function only starts logging once the ExtensionManager * is available, which means it will not fire for the first couple of * queries that set the character set. * * @since Symphony 2.3 * @delegate PostQueryExecution * @param string $context * '/frontend/' or '/backend/' * @param string $query * The query that has just been executed * @param string $query_hash * The hash used by Symphony to uniquely identify this query * @param float $execution_time * The time that it took to run `$query` */ if (self::$_logging === true) { if (Symphony::ExtensionManager() instanceof ExtensionManager) { Symphony::ExtensionManager()->notifyMembers('PostQueryExecution', class_exists('Administration', false) ? '/backend/' : '/frontend/', array('query' => $query, 'query_hash' => $query_hash, 'execution_time' => $stop)); // If the ExceptionHandler is enabled, then the user is authenticated // or we have a serious issue, so log the query. if (GenericExceptionHandler::$enabled) { self::$_log[$query_hash] = array('query' => $query, 'query_hash' => $query_hash, 'execution_time' => $stop); } // Symphony isn't ready yet. Log internally } else { self::$_log[$query_hash] = array('query' => $query, 'query_hash' => $query_hash, 'execution_time' => $stop); } } return true; }
/** * This function creates a new report in the `$_samples` array where the message * is the name of this report. By default, all samples are compared to the `$_starttime` * but if the `PROFILE_LAP` constant is passed, it will be compared to specific `$_seed` * timestamp. Samples can grouped by type (ie. Datasources, Events), but by default * are grouped by 'General'. Optionally, the number of SQL queries that have occurred * since either `$_starttime` or `$_seed` can be passed. Memory usage is taken with each * sample which measures the amount of memory used by this script by PHP at the * time of sampling. * * @param string $msg * A description for this sample * @param integer $type * Either `PROFILE_RUNNING_TOTAL` or `PROFILE_LAP` * @param string $group * Allows samples to be grouped together, defaults to General. * @param integer $queries * The number of MySQL queries that occurred since the `$_starttime` or `$_seed` */ public function sample($msg, $type = PROFILE_RUNNING_TOTAL, $group = 'General', $queries = null) { if ($type == PROFILE_RUNNING_TOTAL) { Profiler::$_samples[] = array($msg, precision_timer('stop', Profiler::$_starttime), precision_timer(), $group, $queries, memory_get_usage()); } else { if (!is_null(Profiler::$_seed)) { $start = Profiler::$_seed; Profiler::$_seed = null; } else { $start = null; } $prev = Profiler::retrieveLast(); Profiler::$_samples[] = array($msg, precision_timer('stop', $start ? $start : $prev[2]), precision_timer(), $group, $queries, memory_get_usage()); } }
define('CORE', DOCROOT . '/symphony'); define('CAMPFIRE', DOCROOT . '/campfire'); define('WORKSPACE', DOCROOT . '/workspace'); define('LIBRARY', CORE . '/lib'); define('AJAX', CORE . '/ajax'); define('UPLOAD', WORKSPACE . '/upload'); define('DATASOURCES', WORKSPACE . '/data-sources'); define('EVENTS', WORKSPACE . '/events'); define('TEXTFORMATTERS', WORKSPACE . '/text-formatters'); define('CACHE', MANIFEST . '/cache'); define('TMP', MANIFEST . '/tmp'); define('LOGS', MANIFEST . '/logs'); define('CONFIG', MANIFEST . '/config.php'); define('TOOLKIT', LIBRARY . '/toolkit'); define('LANG', LIBRARY . '/lang'); define('STARTTIME', precision_timer()); define('TWO_WEEKS', 72576000); //2 weeks in seconds. Used by cookies. define('CACHE_LIFETIME', TWO_WEEKS); define('HTTPS', $_SERVER['HTTPS']); define('HTTP_HOST', $_SERVER['HTTP_HOST']); define('REMOTE_ADDR', $_SERVER['REMOTE_ADDR']); define('HTTP_USER_AGENT', $_SERVER['HTTP_USER_AGENT']); define('__SECURE__', HTTPS == 'on'); if (!defined('URL')) { define('URL', 'http' . (defined('__SECURE__') && __SECURE__ ? 's' : '') . '://' . DOMAIN); } define('_CURL_AVAILABLE_', function_exists('curl_init')); if (function_exists('domxml_xslt_stylesheet')) { define('_USING_DOMXML_XSLT_', true); define('_XSLT_AVAILABLE_', true);
/** * Takes an SQL string and executes it. This function will apply query * caching if it is a read operation and if query caching is set. Symphony * will convert the `tbl_` prefix of tables to be the one set during installation. * A type parameter is provided to specify whether `$this->_lastResult` will be an array * of objects or an array of associative arrays. The default is objects. This * function will return boolean, but set `$this->_lastResult` to the result. * * @param string $query * The full SQL query to execute. * @param string $type * Whether to return the result as objects or associative array. Defaults * to OBJECT which will return objects. The other option is ASSOC. If $type * is not either of these, it will return objects. * @return boolean * True if the query executed without errors, false otherwise */ public function query($query, $type = "OBJECT") { if (empty($query)) { return false; } $query = trim($query); $query_type = $this->determineQueryType($query); if (MySQL::$_connection['tbl_prefix'] != 'tbl_') { $query = preg_replace('/tbl_(\\S+?)([\\s\\.,]|$)/', MySQL::$_connection['tbl_prefix'] . '\\1\\2', $query); } // TYPE is deprecated since MySQL 4.0.18, ENGINE is preferred if ($query_type == MySQL::__WRITE_OPERATION__) { $query = preg_replace('/TYPE=(MyISAM|InnoDB)/i', 'ENGINE=$1', $query); } else { if ($query_type == MySQL::__READ_OPERATION__ && !preg_match('/^SELECT\\s+SQL(_NO)?_CACHE/i', $query)) { if ($this->isCachingEnabled()) { $query = preg_replace('/^SELECT\\s+/i', 'SELECT SQL_CACHE ', $query); } else { $query = preg_replace('/^SELECT\\s+/i', 'SELECT SQL_NO_CACHE ', $query); } } } $query_hash = md5($query . microtime()); self::$_log['query'][$query_hash] = array('query' => $query, 'start' => precision_timer()); $this->flush(); $this->_lastQuery = $query; $this->_result = mysql_query($query, MySQL::$_connection['id']); self::$_query_count++; if (mysql_error()) { $this->__error(); } else { if (is_resource($this->_result)) { if ($type == "ASSOC") { while ($row = mysql_fetch_assoc($this->_result)) { $this->_lastResult[] = $row; } } else { while ($row = mysql_fetch_object($this->_result)) { $this->_lastResult[] = $row; } } mysql_free_result($this->_result); } } self::$_log['query'][$query_hash]['time'] = precision_timer('stop', self::$_log['query'][$query_hash]['start']); return true; }
/** * This function sets the page's parameters, processes the Datasources and * Events and sets the `$xml` and `$xsl` variables. This functions resolves the `$page` * by calling the `resolvePage()` function. If a page is not found, it attempts * to locate the Symphony 404 page set in the backend otherwise it throws * the default Symphony 404 page. If the page is found, the page's XSL utility * is found, and the system parameters are set, including any URL parameters, * params from the Symphony cookies. Events and Datasources are executed and * any parameters generated by them are appended to the existing parameters * before setting the Page's XML and XSL variables are set to the be the * generated XML (from the Datasources and Events) and the XSLT (from the * file attached to this Page) * * @uses FrontendPageResolved * @uses FrontendParamsResolve * @uses FrontendParamsPostResolve * @see resolvePage() */ private function __buildPage() { $start = precision_timer(); if (!($page = $this->resolvePage())) { throw new FrontendPageNotFoundException(); } /** * Just after having resolved the page, but prior to any commencement of output creation * @delegate FrontendPageResolved * @param string $context * '/frontend/' * @param FrontendPage $page * An instance of this class, passed by reference * @param array $page_data * An associative array of page data, which is a combination from `tbl_pages` and * the path of the page on the filesystem. Passed by reference */ Symphony::ExtensionManager()->notifyMembers('FrontendPageResolved', '/frontend/', array('page' => &$this, 'page_data' => &$page)); $this->_pageData = $page; $path = explode('/', $page['path']); $root_page = is_array($path) ? array_shift($path) : $path; $current_path = explode(dirname($_SERVER['SCRIPT_NAME']), $_SERVER['REQUEST_URI'], 2); $current_path = '/' . ltrim(end($current_path), '/'); $split_path = explode('?', $current_path, 3); $current_path = rtrim(current($split_path), '/'); $querystring = '?' . next($split_path); // Get max upload size from php and symphony config then choose the smallest $upload_size_php = ini_size_to_bytes(ini_get('upload_max_filesize')); $upload_size_sym = Symphony::Configuration()->get('max_upload_size', 'admin'); $this->_param = array('today' => DateTimeObj::get('Y-m-d'), 'current-time' => DateTimeObj::get('H:i'), 'this-year' => DateTimeObj::get('Y'), 'this-month' => DateTimeObj::get('m'), 'this-day' => DateTimeObj::get('d'), 'timezone' => DateTimeObj::get('P'), 'website-name' => Symphony::Configuration()->get('sitename', 'general'), 'page-title' => $page['title'], 'root' => URL, 'workspace' => URL . '/workspace', 'root-page' => $root_page ? $root_page : $page['handle'], 'current-page' => $page['handle'], 'current-page-id' => $page['id'], 'current-path' => $current_path, 'parent-path' => '/' . $page['path'], 'current-query-string' => XMLElement::stripInvalidXMLCharacters(utf8_encode(urldecode($querystring))), 'current-url' => URL . $current_path, 'upload-limit' => min($upload_size_php, $upload_size_sym), 'symphony-version' => Symphony::Configuration()->get('version', 'symphony')); if (is_array($this->_env['url'])) { foreach ($this->_env['url'] as $key => $val) { $this->_param[$key] = $val; } } if (is_array($_GET) && !empty($_GET)) { foreach ($_GET as $key => $val) { if (in_array($key, array('symphony-page', 'debug', 'profile'))) { continue; } // If the browser sends encoded entities for &, ie. a=1&b=2 // this causes the $_GET to output they key as amp;b, which results in // $url-amp;b. This pattern will remove amp; allow the correct param // to be used, $url-b $key = preg_replace('/(^amp;|\\/)/', null, $key); // If the key gets replaced out then it will break the XML so prevent // the parameter being set. if (!General::createHandle($key)) { continue; } $this->_param['url-' . $key] = XMLElement::stripInvalidXMLCharacters(utf8_encode(urldecode($val))); } } if (is_array($_COOKIE[__SYM_COOKIE_PREFIX_]) && !empty($_COOKIE[__SYM_COOKIE_PREFIX_])) { foreach ($_COOKIE[__SYM_COOKIE_PREFIX_] as $key => $val) { $this->_param['cookie-' . $key] = $val; } } // Flatten parameters: General::flattenArray($this->_param); /** * Just after having resolved the page params, but prior to any commencement of output creation * @delegate FrontendParamsResolve * @param string $context * '/frontend/' * @param array $params * An associative array of this page's parameters */ Symphony::ExtensionManager()->notifyMembers('FrontendParamsResolve', '/frontend/', array('params' => &$this->_param)); $xml_build_start = precision_timer(); $xml = new XMLElement('data'); $xml->setIncludeHeader(true); $events = new XMLElement('events'); $this->processEvents($page['events'], $events); $xml->appendChild($events); $this->_events_xml = clone $events; $this->processDatasources($page['data_sources'], $xml); Symphony::Profiler()->seed($xml_build_start); Symphony::Profiler()->sample('XML Built', PROFILE_LAP); if (is_array($this->_env['pool']) && !empty($this->_env['pool'])) { foreach ($this->_env['pool'] as $handle => $p) { if (!is_array($p)) { $p = array($p); } foreach ($p as $key => $value) { if (is_array($value) && !empty($value)) { foreach ($value as $kk => $vv) { $this->_param[$handle] .= @implode(', ', $vv) . ','; } } else { $this->_param[$handle] = @implode(', ', $p); } } $this->_param[$handle] = trim($this->_param[$handle], ','); } } /** * Access to the resolved param pool, including additional parameters provided by Data Source outputs * @delegate FrontendParamsPostResolve * @param string $context * '/frontend/' * @param array $params * An associative array of this page's parameters */ Symphony::ExtensionManager()->notifyMembers('FrontendParamsPostResolve', '/frontend/', array('params' => &$this->_param)); $params = new XMLElement('params'); foreach ($this->_param as $key => $value) { // To support multiple parameters using the 'datasource.field' // we will pop off the field handle prior to sanitizing the // key. This is because of a limitation where General::createHandle // will strip '.' as it's technically punctuation. if (strpos($key, '.') !== false) { $parts = explode('.', $key); $field_handle = '.' . array_pop($parts); $key = implode('', $parts); } else { $field_handle = ''; } $key = Lang::createHandle($key) . $field_handle; $param = new XMLElement($key); // DS output params get flattened to a string, so get the original pre-flattened array if (isset($this->_env['pool'][$key])) { $value = $this->_env['pool'][$key]; } if (is_array($value) && !(count($value) == 1 && empty($value[0]))) { foreach ($value as $key => $value) { $item = new XMLElement('item', General::sanitize($value)); $item->setAttribute('handle', Lang::createHandle($value)); $param->appendChild($item); } } else { if (is_array($value)) { $param->setValue(General::sanitize($value[0])); } else { $param->setValue(General::sanitize($value)); } } $params->appendChild($param); } $xml->prependChild($params); Symphony::Profiler()->seed(); $this->setXML($xml->generate(true, 0)); Symphony::Profiler()->sample('XML Generation', PROFILE_LAP); $xsl = '<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:import href="./workspace/pages/' . basename($page['filelocation']) . '"/> </xsl:stylesheet>'; $this->setXSL($xsl, false); $this->setRuntimeParam($this->_param); Symphony::Profiler()->seed($start); Symphony::Profiler()->sample('Page Built', PROFILE_LAP); }
$cache = new Cacheable(Symphony::Database()); $cachedData = $cache->check($cache_id); $writeToCache = false; $valid = true; $creation = DateTimeObj::get('c'); $timeout = isset($this->dsParamTIMEOUT) ? (int) max(1, $this->dsParamTIMEOUT) : 6; if (!is_array($cachedData) || empty($cachedData) || time() - $cachedData['creation'] > $this->dsParamCACHE * 60) { if (Mutex::acquire($cache_id, $timeout, TMP)) { $start = precision_timer(); $ch = new Gateway(); $ch->init(); $ch->setopt('URL', $this->dsParamURL); $ch->setopt('TIMEOUT', $timeout); $xml = $ch->exec(); $writeToCache = true; $end = precision_timer('stop', $start); $info = $ch->getInfoLast(); Mutex::release($cache_id, TMP); $xml = trim($xml); // Handle any response that is not a 200, or the content type does not include xml, plain or text if ((int) $info['http_code'] != 200 || !preg_match('/(xml|plain|text)/i', $info['content_type'])) { $writeToCache = false; if (is_array($cachedData) && !empty($cachedData)) { $xml = trim($cachedData['data']); $valid = false; $creation = DateTimeObj::get('c', $cachedData['creation']); } else { $result->setAttribute('valid', 'false'); if ($end > $timeout) { $result->appendChild(new XMLElement('error', sprintf('Request timed out. %d second limit reached.', $timeout))); } else {
$Author = new Author($Parent, $author_id); } else { $xml = new XMLElement('error', 'You do not have permission to access this page'); } if (is_object($Author)) { ##Run requested script, returning an error if the action was not found if (@is_file(AJAX . "/ajax." . $_REQUEST['action'] . ".php")) { $xml = new XMLElement($_REQUEST['action']); include_once AJAX . "/ajax." . $_REQUEST['action'] . ".php"; } else { $action_parts = preg_split('/\\//', $_REQUEST['action'], -1, PREG_SPLIT_NO_EMPTY); $action_path = str_replace(end($action_parts) . '/', '', $_REQUEST['action']); $action_name = rtrim(str_replace($action_path, '', $_REQUEST['action']), '/'); $file_path = CAMPFIRE . $action_path . "/ajax/ajax." . $action_name . ".php"; if (@is_file($file_path)) { $xml = new XMLElement($action_name); include_once $file_path; } else { $xml = new XMLElement("error", "Ajax action '" . $_REQUEST['action'] . "' does not exist."); } } } #Close the database connections @$db->close(); #Record the render time $rendertime = precision_timer("stop", STARTTIME); ##XML is returned, make sure the browser knows it header("Content-Type: text/xml"); $xml->setIncludeHeader(true); print $xml->generate(true); exit;
public function query($query) { if (empty($query)) { return false; } $query = trim($query); $query_type = $this->determineQueryType($query); if ($query_type == self::__READ_OPERATION__ && $this->isCachingEnabled() !== NULL && !preg_match('/^SELECT\\s+SQL(_NO)?_CACHE/i', $query)) { if ($this->isCachingEnabled() === false) { $query = preg_replace('/^SELECT\\s+/i', 'SELECT SQL_NO_CACHE ', $query); } elseif ($this->isCachingEnabled() === true) { $query = preg_replace('/^SELECT\\s+/i', 'SELECT SQL_CACHE ', $query); } } if ($this->_connection['tbl_prefix'] != 'tbl_') { $query = preg_replace('/tbl_(\\S+?)([\\s\\.,]|$)/', $this->_connection['tbl_prefix'] . '\\1\\2', $query); } $query_hash = md5($query . time()); $this->_log['query'][$query_hash] = array('query' => $query, 'start' => precision_timer()); $this->flush(); $this->_lastQuery = $query; $this->_result = @mysql_query($query, $this->_connection['id']); $this->_query_count++; if (@mysql_error()) { $this->__error(); return false; } while ($row = @mysql_fetch_object($this->_result)) { @array_push($this->_lastResult, $row); } if ($query_type == self::__WRITE_OPERATION__) { $this->_affectedRows = @mysql_affected_rows(); if (stristr($query, 'insert') || stristr($query, 'replace')) { $this->_insertID = @mysql_insert_id($this->_connection['id']); } } @mysql_free_result($this->_result); $this->_log['query'][$query_hash]['time'] = precision_timer('stop', $this->_log['query'][$query_hash]['start']); if ($this->_logEverything) { $this->_log['query'][$query_hash]['lastResult'] = $this->_lastResult; } return true; }
private static function __precisionTimer($action = 'start', $start_time = null) { return precision_timer($action, $start_time); }
# Delegate: SetTemplate # Description: Template has been set, but this delegate will let you change it to whatever you wish $CampfireManager->notifyMembers('SetTemplate', CURRENTPAGE, array('template' => &$template)); ob_start(); include $Admin->getContent(CURRENTPAGE); $content = !defined("__SYM_FATAL_ERROR__") ? ob_get_contents() : __SYM_FATAL_ERROR__; ob_end_clean(); #### # Delegate: GenerateHead # Description: All head information is generated just after this call. This is where you can remove/add items $CampfireManager->notifyMembers('GenerateHead', CURRENTPAGE); $Admin->generateHeadExtras(); ob_start(); include CORE . "/template/" . $template . ".php"; $final_page = ob_get_clean(); $replace = array("<!-- CONTENT -->" => $content, "<!-- RENDER TIME -->" => precision_timer("stop", STARTTIME), "<!-- PAGE TITLE -->" => $GLOBALS['pageTitle'], "<!-- ONLOAD EVENT -->" => $GLOBALS['onloadEvent'], "<!-- HEAD EXTRAS -->" => "\t" . @implode("\n\t", $GLOBALS['headExtras'])); $final_page = str_replace(array_keys($replace), array_values($replace), $final_page); $Admin->processLogs(); if ($template == "login" || $template == "update") { $final_page = str_replace("<!-- ERRORS -->", defined("__SYM_ERROR_MESSAGE__") ? "<p>" . __SYM_ERROR_MESSAGE__ . "</p>" : "", $final_page); } else { $error = NULL; if (defined('__SYM_ERROR_MESSAGE__')) { $error = '<p id="notice" class="error">' . __SYM_ERROR_MESSAGE__ . '</p>'; } elseif (defined('__SYM_NOTICE_MESSAGE__')) { $error = '<p id="notice">' . __SYM_NOTICE_MESSAGE__ . '</p>'; } $final_page = str_replace("<!-- ERRORS -->", $error ? $error : "", $final_page); } $profiler->sample("Page Render Time"); ##Generate page headers
/** * Given an array of all the Datasources for this page, sort them into the * correct execution order and append the Datasource results to the * page XML. If the Datasource provides any parameters, they will be * added to the `$env` pool for use by other Datasources and eventual * inclusion into the page parameters. * * @param string $datasources * A string of Datasource's attached to this page, comma separated. * @param XMLElement $wrapper * The XMLElement to append the Datasource results to. Datasource * results are contained in a root XMLElement that is the handlised * version of their name. * @param array $params * Any params to automatically add to the `$env` pool, by default this * is an empty array. It looks like Symphony does not utilise this parameter * at all * @throws Exception */ public function processDatasources($datasources, XMLElement &$wrapper, array $params = array()) { if (trim($datasources) == '') { return; } $datasources = preg_split('/,\\s*/i', $datasources, -1, PREG_SPLIT_NO_EMPTY); $datasources = array_map('trim', $datasources); if (!is_array($datasources) || empty($datasources)) { return; } $this->_env['pool'] = $params; $pool = $params; $dependencies = array(); foreach ($datasources as $handle) { $pool[$handle] = DatasourceManager::create($handle, array(), false); $dependencies[$handle] = $pool[$handle]->getDependencies(); } $dsOrder = $this->__findDatasourceOrder($dependencies); foreach ($dsOrder as $handle) { $startTime = precision_timer(); $queries = Symphony::Database()->queryCount(); // default to no XML $xml = null; $ds = $pool[$handle]; // Handle redirect on empty setting correctly RE: #1539 try { $ds->processParameters(array('env' => $this->_env, 'param' => $this->_param)); } catch (FrontendPageNotFoundException $e) { // Work around. This ensures the 404 page is displayed and // is not picked up by the default catch() statement below FrontendPageNotFoundExceptionHandler::render($e); } /** * Allows extensions to execute the data source themselves (e.g. for caching) * and providing their own output XML instead * * @since Symphony 2.3 * @delegate DataSourcePreExecute * @param string $context * '/frontend/' * @param DataSource $datasource * The Datasource object * @param mixed $xml * The XML output of the data source. Can be an `XMLElement` or string. * @param array $param_pool * The existing param pool including output parameters of any previous data sources */ Symphony::ExtensionManager()->notifyMembers('DataSourcePreExecute', '/frontend/', array('datasource' => &$ds, 'xml' => &$xml, 'param_pool' => &$this->_env['pool'])); // if the XML is still null, an extension has not run the data source, so run normally if (is_null($xml)) { $xml = $ds->grab($this->_env['pool']); } if ($xml) { /** * After the datasource has executed, either by itself or via the * `DataSourcePreExecute` delegate, and if the `$xml` variable is truthy, * this delegate allows extensions to modify the output XML and parameter pool * * @since Symphony 2.3 * @delegate DataSourcePostExecute * @param string $context * '/frontend/' * @param DataSource $datasource * The Datasource object * @param mixed $xml * The XML output of the data source. Can be an `XMLElement` or string. * @param array $param_pool * The existing param pool including output parameters of any previous data sources */ Symphony::ExtensionManager()->notifyMembers('DataSourcePostExecute', '/frontend/', array('datasource' => $ds, 'xml' => &$xml, 'param_pool' => &$this->_env['pool'])); if ($xml instanceof XMLElement) { $wrapper->appendChild($xml); } else { $wrapper->appendChild(' ' . trim($xml) . PHP_EOL); } } $queries = Symphony::Database()->queryCount() - $queries; Symphony::Profiler()->seed($startTime); Symphony::Profiler()->sample($handle, PROFILE_LAP, 'Datasource', $queries); unset($ds); } }
$result = NULL; $creation = DateTimeObj::get('c'); $timeout = 6; if (isset($this->dsParamTIMEOUT)) { $timeout = (int) max(1, $this->dsParamTIMEOUT); } if (!is_array($cachedData) || empty($cachedData) || time() - $cachedData['creation'] > $this->dsParamCACHE * 60) { if (Mutex::acquire($cache_id, $timeout, TMP)) { $start = precision_timer(); $ch = new Gateway(); $ch->init(); $ch->setopt('URL', $this->dsParamURL); $ch->setopt('TIMEOUT', $timeout); $xml = $ch->exec(); $writeToCache = true; $end = precision_timer('STOP', $start); $info = $ch->getInfoLast(); Mutex::release($cache_id, TMP); $xml = trim($xml); if ((int) $info['http_code'] != 200 || !preg_match('/(xml|plain|text)/i', $info['content_type'])) { $writeToCache = false; if (is_array($cachedData) && !empty($cachedData)) { $xml = trim($cachedData['data']); $valid = false; $creation = DateTimeObj::get('c', $cachedData['creation']); } else { $result = new XMLElement($this->dsParamROOTELEMENT); $result->setAttribute('valid', 'false'); if ($end > $timeout) { $result->appendChild(new XMLElement('error', sprintf('Request timed out. %d second limit reached.', $timeout))); } else {
public function render(Register $ParameterOutput) { $result = null; $doc = new XMLDocument(); if (isset($this->parameters()->url)) { $this->parameters()->url = self::replaceParametersInString($this->parameters()->url, $ParameterOutput); } if (isset($this->parameters()->xpath)) { $this->parameters()->xpath = self::replaceParametersInString($this->parameters()->xpath, $ParameterOutput); } $cache_id = md5($this->parameters()->url . serialize($this->parameters()->namespaces) . $this->parameters()->xpath); $cache = Cache::instance(); $cachedData = $cache->read($cache_id); $writeToCache = false; $force_empty_result = false; $valid = true; $result = NULL; $creation = DateTimeObj::get('c'); if (isset($this->parameters()->timeout)) { $timeout = (int) max(1, $this->parameters()->timeout); } if (!is_array($cachedData) || empty($cachedData) || time() - $cachedData['creation'] > $this->parameters()->{'cache-timeout'} * 60) { if (Mutex::acquire($cache_id, $timeout, TMP)) { $start = precision_timer(); $ch = new Gateway(); $ch->init(); $ch->setopt('URL', $this->parameters()->url); $ch->setopt('TIMEOUT', $this->parameters()->timeout); $xml = $ch->exec(); $writeToCache = true; $end = precision_timer('STOP', $start); $info = $ch->getInfoLast(); Mutex::release($cache_id, TMP); $xml = trim($xml); if ((int) $info['http_code'] != 200 || !preg_match('/(xml|plain|text)/i', $info['content_type'])) { $writeToCache = false; if (is_array($cachedData) && !empty($cachedData)) { $xml = trim($cachedData['data']); $valid = false; $creation = DateTimeObj::get('c', $cachedData['creation']); } else { $result = $doc->createElement($this->parameters()->{'root-element'}); $result->setAttribute('valid', 'false'); if ($end > $timeout) { $result->appendChild($doc->createElement('error', sprintf('Request timed out. %d second limit reached.', $timeout))); } else { $result->appendChild($doc->createElement('error', sprintf('Status code %d was returned. Content-type: %s', $info['http_code'], $info['content_type']))); } return $result; } } elseif (strlen($xml) > 0 && !General::validateXML($xml, $errors)) { $writeToCache = false; if (is_array($cachedData) && !empty($cachedData)) { $xml = trim($cachedData['data']); $valid = false; $creation = DateTimeObj::get('c', $cachedData['creation']); } else { $result = $doc->createElement($this->parameters()->{'root-element'}, $doc->createElement('error', __('XML returned is invalid.')), array('valid' => 'false')); return $result; } } elseif (strlen($xml) == 0) { $force_empty_result = true; } } elseif (is_array($cachedData) && !empty($cachedData)) { $xml = trim($cachedData['data']); $valid = false; $creation = DateTimeObj::get('c', $cachedData['creation']); if (empty($xml)) { $force_empty_result = true; } } else { $force_empty_result = true; } } else { $xml = trim($cachedData['data']); $creation = DateTimeObj::get('c', $cachedData['creation']); } if (!$force_empty_result) { $result = new XMLDocument(); $root = $result->createElement($this->parameters()->{'root-element'}); //XPath Approach, saves Transforming the Document. $xDom = new XMLDocument(); $xDom->loadXML($xml); if ($xDom->hasErrors()) { $root->setAttribute('valid', 'false'); $root->appendChild($result->createElement('error', __('XML returned is invalid.'))); $messages = $result->createElement('messages'); foreach ($xDom->getErrors() as $e) { if (strlen(trim($e->message)) == 0) { continue; } $messages->appendChild($result->createElement('item', General::sanitize($e->message))); } $root->appendChild($messages); } else { if ($writeToCache) { $cache->write($cache_id, $xml); } $xpath = new DOMXPath($xDom); ## Namespaces if (is_array($this->parameters()->namespaces) && !empty($this->parameters()->namespaces)) { foreach ($this->parameters()->namespaces as $index => $namespace) { $xpath->registerNamespace($namespace['name'], $namespace['uri']); } } $xpath_list = $xpath->query($this->parameters()->xpath); foreach ($xpath_list as $node) { if ($node instanceof XMLDocument) { $root->appendChild($result->importNode($node->documentElement, true)); } else { $root->appendChild($result->importNode($node, true)); } } $root->setAttribute('status', $valid === true ? 'fresh' : 'stale'); $root->setAttribute('creation', $creation); } } if (!$root->hasChildNodes() || $force_empty_result) { $this->emptyXMLSet($root); } $result->appendChild($root); return $result; }
public function query($query) { if (empty($query)) { return false; } $query = trim($query); $query_type = $this->determineQueryType($query); if ($query_type == self::__READ_OPERATION__ && $this->isCachingEnabled() !== NULL && !preg_match('/^SELECT\\s+SQL(_NO)?_CACHE/i', $query)) { if ($this->isCachingEnabled() === false) { $query = preg_replace('/^SELECT\\s+/i', 'SELECT SQL_NO_CACHE ', $query); } elseif ($this->isCachingEnabled() === true) { $query = preg_replace('/^SELECT\\s+/i', 'SELECT SQL_CACHE ', $query); } } if ($this->_connection['tbl_prefix'] != 'tbl_') { $query = preg_replace('/tbl_(\\S+?)([\\s\\.,]|$)/', $this->_connection['tbl_prefix'] . '\\1\\2', $query); } $query_hash = md5($query . microtime()); self::$_log['query'][$query_hash] = array('query' => $query, 'start' => precision_timer()); $this->flush(); $this->_lastQuery = $query; $this->_result = mysql_query($query, $this->_connection['id']); self::$_query_count++; if (mysql_error()) { $this->__error(); return false; } if (is_resource($this->_result)) { while ($row = mysql_fetch_object($this->_result)) { $this->_lastResult[] = $row; } mysql_free_result($this->_result); } self::$_log['query'][$query_hash]['time'] = precision_timer('stop', self::$_log['query'][$query_hash]['start']); if ($this->_logEverything) { self::$_log['query'][$query_hash]['lastResult'] = $this->_lastResult; } return true; }
private function __buildPage() { $start = precision_timer(); if (!($page = $this->resolvePage())) { $page = $this->_Parent->Database->fetchRow(0, "\n\t\t\t\t\t\t\t\tSELECT `tbl_pages`.* \n\t\t\t\t\t\t\t\tFROM `tbl_pages`, `tbl_pages_types` \n\t\t\t\t\t\t\t\tWHERE `tbl_pages_types`.page_id = `tbl_pages`.id \n\t\t\t\t\t\t\t\tAND tbl_pages_types.`type` = '404' \n\t\t\t\t\t\t\t\tLIMIT 1"); if (empty($page)) { $this->_Parent->customError(E_USER_ERROR, __('Page Not Found'), __('The page you requested does not exist.'), false, true, 'error', array('header' => 'HTTP/1.0 404 Not Found')); } $page['filelocation'] = $this->resolvePageFileLocation($page['path'], $page['handle']); $page['type'] = $this->__fetchPageTypes($page['id']); } #### # Delegate: FrontendPageResolved # Description: Just after having resolved the page, but prior to any commencement of output creation # Global: Yes $this->ExtensionManager->notifyMembers('FrontendPageResolved', '/frontend/', array('page' => &$this, 'page_data' => &$page)); $this->_pageData = $page; $root_page = @array_shift(explode('/', $page['path'])); $current_path = explode(dirname($_SERVER['SCRIPT_NAME']), $_SERVER['REQUEST_URI'], 2); $current_path = '/' . ltrim(end($current_path), '/'); // Get max upload size from php and symphony config then choose the smallest $upload_size_php = ini_size_to_bytes(ini_get('upload_max_filesize')); $upload_size_sym = Frontend::instance()->Configuration->get('max_upload_size', 'admin'); $this->_param = array('today' => DateTimeObj::get('Y-m-d'), 'current-time' => DateTimeObj::get('H:i'), 'this-year' => DateTimeObj::get('Y'), 'this-month' => DateTimeObj::get('m'), 'this-day' => DateTimeObj::get('d'), 'timezone' => DateTimeObj::get('P'), 'website-name' => $this->_Parent->Configuration->get('sitename', 'general'), 'page-title' => $page['title'], 'root' => URL, 'workspace' => URL . '/workspace', 'root-page' => $root_page ? $root_page : $page['handle'], 'current-page' => $page['handle'], 'current-page-id' => $page['id'], 'current-path' => $current_path, 'parent-path' => '/' . $page['path'], 'current-url' => URL . $current_path, 'upload-limit' => min($upload_size_php, $upload_size_sym), 'symphony-build' => $this->_Parent->Configuration->get('build', 'symphony')); if (is_array($this->_env['url'])) { foreach ($this->_env['url'] as $key => $val) { $this->_param[$key] = $val; } } if (is_array($_GET) && !empty($_GET)) { foreach ($_GET as $key => $val) { if (!in_array($key, array('symphony-page', 'debug', 'profile'))) { $this->_param['url-' . $key] = $val; } } } if (is_array($_COOKIE[__SYM_COOKIE_PREFIX_]) && !empty($_COOKIE[__SYM_COOKIE_PREFIX_])) { foreach ($_COOKIE[__SYM_COOKIE_PREFIX_] as $key => $val) { $this->_param['cookie-' . $key] = $val; } } // Flatten parameters: General::flattenArray($this->_param); #### # Delegate: FrontendParamsResolve # Description: Just after having resolved the page params, but prior to any commencement of output creation # Global: Yes $this->ExtensionManager->notifyMembers('FrontendParamsResolve', '/frontend/', array('params' => &$this->_param)); $xml_build_start = precision_timer(); $xml = new XMLElement('data'); $xml->setIncludeHeader(true); $events = new XMLElement('events'); $this->__processEvents($page['events'], $events); $xml->appendChild($events); $this->_events_xml = clone $events; $this->__processDatasources($page['data_sources'], $xml); $this->_Parent->Profiler->seed($xml_build_start); $this->_Parent->Profiler->sample('XML Built', PROFILE_LAP); if (is_array($this->_env['pool']) && !empty($this->_env['pool'])) { foreach ($this->_env['pool'] as $handle => $p) { if (!is_array($p)) { $p = array($p); } foreach ($p as $key => $value) { if (is_array($value) && !empty($value)) { foreach ($value as $kk => $vv) { $this->_param[$handle] .= @implode(', ', $vv) . ','; } } else { $this->_param[$handle] = @implode(', ', $p); } } $this->_param[$handle] = trim($this->_param[$handle], ','); } } #### # Delegate: FrontendParamsPostResolve # Description: Access to the resolved param pool, including additional parameters provided by Data Source outputs # Global: Yes $this->ExtensionManager->notifyMembers('FrontendParamsPostResolve', '/frontend/', array('params' => $this->_param)); ## TODO: Add delegate for adding/removing items in the params $xsl = '<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:import href="./workspace/pages/' . basename($page['filelocation']) . '"/> </xsl:stylesheet>'; $this->_Parent->Profiler->seed(); $this->setXML($xml->generate(true, 0)); $this->_Parent->Profiler->sample('XML Generation', PROFILE_LAP); $this->setXSL($xsl, false); $this->setRuntimeParam($this->_param); $this->_Parent->Profiler->seed($start); $this->_Parent->Profiler->sample('Page Built', PROFILE_LAP); }
/** * Executes the request using Curl unless it is not available * or this function has explicitly been told not by providing * the `Gateway::FORCE_SOCKET` constant as a parameter. The function * will apply all the options set using `curl_setopt` before * executing the request. Information about the transfer is * available using the `getInfoLast()` function. Should Curl not be * available, this function will fallback to using Sockets with `fsockopen` * * @see toolkit.Gateway#getInfoLast() * @param string $force_connection_method * Only one valid parameter, `Gateway::FORCE_SOCKET` * @return string|boolean * The result of the transfer as a string. If any errors occur during * a socket request, false will be returned. */ public function exec($force_connection_method = null) { if ($force_connection_method !== self::FORCE_SOCKET && self::isCurlAvailable()) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, sprintf("%s://%s%s%s", $this->_scheme, $this->_host, !is_null($this->_port) ? ':' . $this->_port : null, $this->_path)); curl_setopt($ch, CURLOPT_HEADER, $this->_returnHeaders); curl_setopt($ch, CURLOPT_USERAGENT, $this->_agent); curl_setopt($ch, CURLOPT_PORT, $this->_port); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, $this->_timeout); if (ini_get('safe_mode') == 0 && ini_get('open_basedir') == '') { curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); } switch ($this->_method) { case 'POST': curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $this->_postfields); break; case 'PUT': curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); curl_setopt($ch, CURLOPT_POSTFIELDS, $this->_postfields); $this->setopt('HTTPHEADER', array('Content-Length:' => strlen($this->_postfields))); break; case 'DELETE': curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); curl_setopt($ch, CURLOPT_POSTFIELDS, $this->_postfields); break; } if (is_array($this->_headers) && !empty($this->_headers)) { curl_setopt($ch, CURLOPT_HTTPHEADER, $this->_headers); } if (is_array($this->_custom_opt) && !empty($this->_custom_opt)) { foreach ($this->_custom_opt as $opt => $value) { curl_setopt($ch, $opt, $value); } } // Grab the result $result = curl_exec($ch); $this->_info_last = curl_getinfo($ch); $this->_info_last['curl_error'] = curl_errno($ch); // Close the connection curl_close($ch); return $result; } $start = precision_timer(); if (is_null($this->_port)) { $this->_port = !is_null($this->_scheme) ? self::$ports[$this->_scheme] : 80; } // No CURL is available, use attempt to use normal sockets $handle = @fsockopen($this->_host, $this->_port, $errno, $errstr, $this->_timeout); if ($handle === false) { return false; } $query = $this->_method . ' ' . $this->_path . ' HTTP/1.1' . PHP_EOL; $query .= 'Host: ' . $this->_host . PHP_EOL; $query .= 'Content-type: ' . $this->_content_type . PHP_EOL; $query .= 'User-Agent: ' . $this->_agent . PHP_EOL; $query .= @implode(PHP_EOL, $this->_headers); $query .= 'Content-length: ' . strlen($this->_postfields) . PHP_EOL; $query .= 'Connection: close' . PHP_EOL . PHP_EOL; if (in_array($this->_method, array('PUT', 'POST', 'DELETE'))) { $query .= $this->_postfields; } // send request if (!@fwrite($handle, $query)) { return false; } stream_set_blocking($handle, false); stream_set_timeout($handle, $this->_timeout); $status = stream_get_meta_data($handle); $response = $dechunked = ''; // get header while (!preg_match('/\\r\\n\\r\\n$/', $header) && !$status['timed_out']) { $header .= @fread($handle, 1); $status = stream_get_meta_data($handle); } $status = socket_get_status($handle); // Get rest of the page data while (!feof($handle) && !$status['timed_out']) { $response .= fread($handle, 4096); $status = stream_get_meta_data($handle); } @fclose($handle); $end = precision_timer('stop', $start); if (preg_match('/Transfer\\-Encoding:\\s+chunked\\r\\n/', $header)) { $fp = 0; do { $byte = ''; $chunk_size = ''; do { $chunk_size .= $byte; $byte = substr($response, $fp, 1); $fp++; } while ($byte !== "\r" && $byte !== "\\r"); $chunk_size = hexdec($chunk_size); // convert to real number if ($chunk_size == 0) { break 1; } $fp++; $dechunked .= substr($response, $fp, $chunk_size); $fp += $chunk_size; $fp += 2; } while (true); $response = $dechunked; } // Following code emulates part of the function curl_getinfo() preg_match('/Content-Type:\\s*([^\\r\\n]+)/i', $header, $match); $content_type = $match[1]; preg_match('/HTTP\\/\\d+.\\d+\\s+(\\d+)/i', $header, $match); $status = $match[1]; $this->_info_last = array('url' => $this->_url, 'content_type' => $content_type, 'http_code' => (int) $status, 'total_time' => $end); return ($this->_returnHeaders ? $header : null) . $response; }
function seed($time = NULL) { $this->_seed = $time ? $time : precision_timer(); }