Файл: library/XenForo/CssOutput.php
Строк: 418
<?php
/**
* Class to output CSS data quickly for public facing pages. This class
* is not designed to be used with the MVC structure; this allows us to
* significantly reduce the amount of overhead in a request.
*
* This class is entirely self sufficient. It handles parsing the input,
* getting the data, rendering it, and manipulating HTTP headers.
*
* @package XenForo_CssOutput
*/
class XenForo_CssOutput
{
/**
* Style ID the CSS will be retrieved from.
*
* @var integer
*/
protected $_styleId = 0;
/**
* Array of CSS templates that have been requested. These will have ".css" appended
* to them and requested as templates.
*
* @var array
*/
protected $_cssRequested = array();
/**
* The timestamp of the last modification, according to the input. (Used to compare
* to If-Modified-Since header.)
*
* @var integer
*/
protected $_inputModifiedDate = 0;
/**
* The direction in which text should be rendered. Either ltr or rtl.
*
* @var string
*/
protected $_textDirection = 'LTR';
/**
* Date of the last modification to the style. Used to output Last-Modified header.
*
* @var integer
*/
protected $_styleModifiedDate = 0;
/**
* List of user display styles to write out username CSS.
*
* @var array
*/
protected $_displayStyles = array();
/**
* List of smilie sprite styles to write out sprite CSS.
*
* @var array
*/
protected $_smilieSprites = array();
/**
* Constructor.
*
* @param array $input Array of input. Style and CSS will be pulled from this.
*/
public function __construct(array $input)
{
$this->parseInput($input);
}
/**
* Parses the style ID and the list of CSS out of the specified array of input.
* The style ID will be found in "style" and CSS list in "css". The CSS should be
* comma-delimited.
*
* @param array $input
*/
public function parseInput(array $input)
{
$this->_styleId = isset($input['style']) ? intval($input['style']) : 0;
if (!empty($input['css']))
{
$css = is_scalar($input['css']) ? strval($input['css']) : '';
if (preg_match('/./u', $css))
{
$this->_cssRequested = explode(',', $css);
}
}
if (!empty($input['d']))
{
$this->_inputModifiedDate = intval($input['d']);
}
if (!empty($input['dir']) && is_string($input['dir']) && strtoupper($input['dir']) == 'RTL')
{
$this->_textDirection = 'RTL';
}
}
public function handleIfModifiedSinceHeader(array $server)
{
$outputCss = true;
if (isset($server['HTTP_IF_MODIFIED_SINCE']))
{
$modDate = strtotime($server['HTTP_IF_MODIFIED_SINCE']);
if ($modDate !== false && $this->_inputModifiedDate <= $modDate)
{
header('Content-type: text/css; charset=utf-8', true, 304);
$outputCss = false;
}
}
return $outputCss;
}
/**
* Does any preparations necessary for outputting to be done.
*/
protected function _prepareForOutput()
{
$this->_displayStyles = XenForo_Application::get('displayStyles');
$styles = XenForo_Application::get('styles');
$smilieSprites = XenForo_Model::create('XenForo_Model_DataRegistry')->get('smilieSprites');
if (is_array($smilieSprites))
{
$this->_smilieSprites = $smilieSprites;
}
if ($this->_styleId && isset($styles[$this->_styleId]))
{
$style = $styles[$this->_styleId];
}
else
{
$style = reset($styles);
}
if ($style)
{
$properties = unserialize($style['properties']);
$this->_styleId = $style['style_id'];
$this->_styleModifiedDate = $style['last_modified_date'];
}
else
{
$properties = array();
$this->_styleId = 0;
}
$defaultProperties = XenForo_Application::get('defaultStyleProperties');
XenForo_Template_Helper_Core::setStyleProperties(XenForo_Application::mapMerge($defaultProperties, $properties), false);
XenForo_Template_Public::setStyleId($this->_styleId);
XenForo_Template_Abstract::setLanguageId(0);
}
/**
* Renders the CSS and returns it.
*
* @return string
*/
public function renderCss()
{
$cacheId = 'xfCssCache_' . sha1(
'style=' . $this->_styleId .
'css=' . serialize($this->_cssRequested) .
'd=' . $this->_inputModifiedDate .
'dir=' . $this->_textDirection .
'minify=' . XenForo_Application::get('options')->minifyCss)
. (XenForo_Application::debugMode() ? 'debug' : '');
if ($cacheObject = XenForo_Application::getCache())
{
if ($cacheCss = $cacheObject->load($cacheId, true))
{
return $cacheCss . "n/* CSS returned from cache. */";
}
}
$this->_prepareForOutput();
if (XenForo_Application::isRegistered('bbCode'))
{
$bbCodeCache = XenForo_Application::get('bbCode');
}
else
{
$bbCodeCache = XenForo_Model::create('XenForo_Model_BbCode')->getBbCodeCache();
}
$params = array(
'displayStyles' => $this->_displayStyles,
'smilieSprites' => $this->_smilieSprites,
'customBbCodes' => !empty($bbCodeCache['bbCodes']) ? $bbCodeCache['bbCodes'] : array(),
'xenOptions' => XenForo_Application::get('options')->getOptions(),
'dir' => $this->_textDirection,
'pageIsRtl' => ($this->_textDirection == 'RTL')
);
$templates = array();
foreach ($this->_cssRequested AS $cssName)
{
$cssName = trim($cssName);
if (!$cssName)
{
continue;
}
$templateName = $cssName . '.css';
if (!isset($templates[$templateName]))
{
$templates[$templateName] = new XenForo_Template_Public($templateName, $params);
}
}
$css = self::renderCssFromObjects($templates, XenForo_Application::debugMode());
$css = self::prepareCssForOutput(
$css,
$this->_textDirection,
(XenForo_Application::get('options')->minifyCss && $cacheObject)
);
if ($cacheObject)
{
$cacheObject->save($css, $cacheId, array(), 86400);
}
return $css;
}
public static function prepareCssForOutput($css, $direction, $minify = false)
{
$css = self::translateCssRules($css);
if ($direction == 'RTL')
{
$css = XenForo_Template_Helper_RightToLeft::getRtlCss($css);
}
$css = preg_replace('/rtl-raw.([a-zA-Z0-9-]+s*:)/', '$1', $css);
if ($minify)
{
$css = Minify_CSS_Compressor::process($css);
}
return $css;
}
/**
* Renders the CSS from a collection of Template objects.
*
* @param array $templates Array of XenForo_Template_Abstract objects
* @param boolean $withDebug If true, output debug CSS when invalid properties are accessed
*
* @return string
*/
public static function renderCssFromObjects(array $templates, $withDebug = false)
{
$errors = array();
$output = '@CHARSET "UTF-8";' . "n";
foreach ($templates AS $templateName => $template)
{
if ($withDebug)
{
XenForo_Template_Helper_Core::resetInvalidStylePropertyAccessList();
}
$rendered = $template->render();
if ($rendered !== '')
{
$output .= "n/* --- " . str_replace('*/', '', $templateName) . " --- */nn$renderedn";
}
if ($withDebug)
{
$propertyError = self::createDebugErrorString(
XenForo_Template_Helper_Core::getInvalidStylePropertyAccessList()
);
if ($propertyError)
{
$errors["$templateName"] = $propertyError;
}
}
}
if ($withDebug && $errors)
{
$output .= self::getDebugErrorsAsCss($errors);
}
return $output;
}
/**
* Translates CSS rules for use by current browsers.
*
* @param string $output
*
* @return string
*/
public static function translateCssRules($output)
{
/**
* CSS3 temporary attributes translation.
* Some browsers implement custom attributes that refer to a future spec.
* This takes the (assumed) future attribute and translates it into
* browser-specific tags, so the CSS can be up to date with browser changes.
*
* @var array CSS translators: key = pattern to find, value = replacement pattern
*/
$cssTranslate = array(
// border/outline-radius
'/(?<=[^a-z0-9-])(border|outline)((-)(top|bottom)(-)(right|left))?-radiuss*:(s*)([^ ;}][^;}]*)s*(?=;|})/siU'
=> '-webkit-13456-radius:78;'
. ' -moz-1-radius346:78;'
. ' -khtml-13456-radius:78;'
. '