/** * Конструктор */ function __construct() { parent::__construct(); if (DEBUG) { Open_Benchmark::getInstance()->mark(__CLASS__ . '_start'); } /** * Создаём объект Smarty */ $this->smarty = new Open_Smarty(); /** * Передаём Smarty ссылки на экземпляры объектов, для удобства их использования изнутри * Использовать функцию Smarty assign_by_ref не надо, т.к. мы и так имеем дело со ссылками на объекты, иначе получится двойная ссылка */ $C = Open_Config::getInstance(); $this->smarty->assign('config', $C); $this->smarty->assign('input', Open_Input::getInstance()); $this->smarty->assign('router', Open_Router::getInstance()); $this->smarty->assign('text', Open_Text::getInstance()); $this->smarty->assign('view', $this); /** * Устанавливаем значения по умолчанию */ $config = $C->get(array('headers', 'default_title', 'js', 'css', 'default_body')); $this->setHeaders($config['headers']); $this->setTitle($config['default_title']); $this->setJs($config['js']); $this->setCss($config['css']); $this->setBody($config['default_body']); }
/** * Конструктор * * @param mixed $value Число (0xFFEEDD), массив (array(0xFF, 0xEE, 0xDD)), либо строка ('#FFEEDD' или название web-цвета) */ function __construct($value) { /** * Если число сохраняем */ if (is_int($value)) { $this->RGB = $value; } else { if (is_array($value) && count($value) == 3) { $temp = array_values($value); $this->RGB = ($temp[0] & 0xff) << 16 | ($temp[1] & 0xff) << 8 | $temp[2] & 0xff; } else { if (is_string($value)) { if ($value[0] == '#') { $this->RGB = intval(base_convert(trim($value, '# '), 16, 10)) & 0xffffff; } else { if (isset(self::$webColors[$temp = strtolower($value)])) { $this->RGB = self::$webColors[$temp]; } } } } } /** * Иначе выдаём ошибку */ if ($this->RGB === FALSE) { triggerError(sprintf(Open_Text::getInstance()->dget('errors', 'The value "<b>%s</b>" cannot be converted to color'), $value), E_USER_WARNING); } }
/** * Конструктор */ protected function __construct() { parent::__construct(); if (DEBUG) { Open_Benchmark::getInstance()->mark(__CLASS__ . '_start'); } $this->config = Open_Config::getInstance(); $this->input = Open_Input::getInstance(); $this->router = Open_Router::getInstance(); $this->text = Open_Text::getInstance(); $this->view = Open_View::getInstance(); $this->setArguments($this->router->getArguments()); }
/** * Получить URL к статическим данным * Если URL является полным (http://www.example.com/eg.css), то так и возвращается * Иначе к нему добавляется префикс в соответствии с типом * * @param string $url * @param int $type Open_View::URL_TYPE_JS или Open_View::URL_TYPE_CSS * @return string */ private function getStaticUrl($url, $type) { $parsedUrl = parse_url($url); if (isset($parsedUrl['scheme']) && isset($parsedUrl['host'])) { return $url; } switch ($type) { case self::URL_TYPE_JS: return '/' . JS_DIR . $url . JSEXT; case self::URL_TYPE_CSS: return '/' . CSS_DIR . $url . CSSEXT; default: trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Type <b>%s</b> is unallowed'), $type), E_USER_WARNING); return $url; } }
/** * Получить параметр из конфига по имени и секции * * @param string $section Имя секции. Если секция не указана, берётся секция по умолчанию * @param string $name Имя параметра либо массив имён параметров. Если не указано, значит имя секции - это имя параметра * @return mixed */ public function get($section, $name = NULL) { /** * Если второй параметр не указан при вызове, значит первый это имя, а не секция * Чтобы при вызове с двумя параметрами сначала была секция, а потом имя параметра, по логике вещей */ if (!isset($name)) { $name = $section; $section = self::DEFAULT_CONFIG; } /** * Если такой секции не существует * То пытаемся подгрузить */ if (!isset($this->config[$section])) { $this->load($section, FALSE); } /** * Если запрашивается массив значений из конфига * Выбираем все по очереди, если чего-то нет, выдаётся ошибка */ if (is_array($name)) { $result = array(); foreach ($name as $value) { if (isset($this->config[$section][$value])) { $result[$value] = $this->config[$section][$value]; } else { trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Config item <b>%s[%s]</b> does not exist'), $section, $name), E_USER_NOTICE); return FALSE; } } return $result; } /** * Если указанный пункт в секции существует, возвращаем */ if (isset($this->config[$section][$name])) { return $this->config[$section][$name]; } /** * Если ничего не найдено, то выдаём ошибку */ trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Config item <b>%s[%s]</b> does not exist'), $section, $name), E_USER_NOTICE); return FALSE; }
/** * Функция для вставки постраничной навигации * * Параметры: * * 1. Обязательные * * 1.1. Для всех шаблонов * 1.1.1. link - ссылка для навигации, где подстрока [:nav:] заменяется на параметр навигации (e.g. страницу, номер объекта) * 1.1.2. amount - количество объектов по которым производится навигация * 1.1.3. span - количество объектов выводимых на одной странице * 1.1.4. current - текущий параметр навигации (e.g. текущая страница, номер объекта) * * 1.2. Шаблон first * * 2. Необязательные * * 2.1. Для всех шаблонов * 2.1.1. pattern - шаблон постраничной навигации. По умолчанию берётся значение 'default_pagination_pattern' из конфига * 2.1.2. class - имя класса для <div> навигации. По умолчанию будет присвоен класс 'pagination-'. $params['pattern'] * * 2.2. Шаблон first * 2.2.1. around - страниц рядом с текущей * 2.2.2. gaps - максимальное количество промежутков * 2.2.3. threshold - порог промежутка * 2.2.4. gap - обозначение промежутка, по умолчанию строка '…', что есть многоточие * * @param array $params * @param object $smarty * @return string */ function smarty_function_pagination($params, &$smarty) { /** * Проверка все ли необходимые аргументы переданы */ if (!isset($params['link']) || empty($params['link'])) { trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Parameter <b>%s</b> for Smarty function <b>pagination</b> is not set'), 'link'), E_USER_ERROR); } else { if (!isset($params['amount']) || empty($params['amount'])) { trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Parameter <b>%s</b> for Smarty function <b>pagination</b> is not set'), 'amount'), E_USER_ERROR); } else { if (!isset($params['span']) || empty($params['span'])) { trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Parameter <b>%s</b> for Smarty function <b>pagination</b> is not set'), 'span'), E_USER_ERROR); } else { if (!isset($params['current']) || empty($params['current'])) { trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Parameter <b>%s</b> for Smarty function <b>pagination</b> is not set'), 'current'), E_USER_ERROR); } } } } /** * Если шаблон не передан, берём значение по умолчанию из конфига */ if (!isset($params['pattern']) || empty($params['pattern'])) { $params['pattern'] = Open_Config::getInstance()->get('default_pagination_pattern'); } /** * Если класс ещё не объявлен, подключается исходный файл */ if (!class_exists('Open_Pagination')) { require_once CORE_PATH . 'Open/Pagination' . EXT; } /** * Выполнение действий для выбранного шаблона */ switch ($params['pattern']) { case Open_Pagination::FIRST: /** * Установка значений по умолчанию, если необходимо */ $params['around'] = isset($params['around']) ? $params['around'] : Open_Pagination::FIRST_DEFAULT_AROUND; $params['gaps'] = isset($params['gaps']) ? $params['gaps'] : Open_Pagination::FIRST_DEFAULT_GAPS; $params['threshold'] = isset($params['threshold']) ? $params['threshold'] : Open_Pagination::FIRST_DEFAULT_THRESHOLD; /** * Получение массива с вычисленными значениями */ $params['pagination'] = Open_Pagination::getInstance()->patternFirst($params['amount'], $params['span'], $params['current'], $params['around'], $params['gaps'], $params['threshold']); break; default: trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Pagination pattern <b>%s</b> is not available'), $params['pattern']), E_USER_ERROR); break; } /** * Получение значения переменной params из Smarty * Устанавливается своё значение */ $paramsBackup = $smarty->get_template_vars('params'); $smarty->assign('params', $params); /** * Отключение кеширования */ $previousCaching = $smarty->caching; $smarty->caching = FALSE; /** * Получение шаблона */ $result = $smarty->fetch('Open/Pagination/' . $params['pattern'] . TPLEXT); /** * Возврат значения кеширования */ $smarty->caching = $previousCaching; /** * Восстановление значения переменной params */ $smarty->assign('params', $paramsBackup); return $result; }
/** * Установить/получить объект идентификатор * Должен соответствовать Open_Auth_Identifier_Interface * При получении если объекта не существует, то по умолчанию будет создан объект Open_Auth_Identifier_Db * * @param object $identifier * @return object */ public function identifier($identifier = NULL) { if (isset($identifier)) { if (!$identifier instanceof Open_Auth_Identifier_Interface) { trigger_error(Open_Text::getInstance()->dget('errors', 'Identifier passed to <b>Open_Auth</b> does not implements <b>Open_Auth_Identifier_Interface</b>'), E_USER_ERROR); } $this->identifier = $identifier; } else { if ($this->identifier === FALSE) { if (!class_exists('Open_Auth_Identifier_Db')) { require_once CORE_PATH . 'Open/Auth/Identifier/Db' . EXT; } $this->identifier(Open_Auth_Identifier_Db::getInstance()); } } return $this->identifier; }
/** * Показать 404-ю * * @param string $message Текст ошибки */ function trigger404($message = FALSE) { if ($message === FALSE) { $message = Open_Text::getInstance()->dget('errors', Open_Config::getInstance()->get('default_404_message')); } triggerError($message, E_404); }
/** * Подключить контроллер, проверить доступность метода, и вызвать * * @param string $controller Контроллер * @param string $method Метод * @param array $arguments Аргументы * @return mixed Возвращённое методом контроллера значение */ private function callControllerMethod($controller, $method, $arguments = array(), $trigger = self::TRIGGER_ERROR) { /** * Получение имени контроллера и метода * Заменяются псевдоразделители '_' на '/' и первая буква каждой части делается заглавной */ $temp = explode('_', $controller); foreach ($temp as &$value) { $value = ucwords(strtolower($value)); } $controller = implode('_', $temp); /** * Проверка объявлен ли класс с таким именем * Если нет, выдаётся ошибка если надо */ if (!class_exists($controller)) { /** * Проверка существует ли файл с контроллером и его подключение */ if (file_exists($temp = CONTROLLERS_PATH . str_replace('_', '/', $controller) . EXT)) { require_once $temp; } else { switch ($trigger) { case self::TRIGGER_404: trigger404(); break; case self::TRIGGER_ERROR: trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Controller <b>%s</b> was not found'), $controller), E_USER_ERROR); default: case self::TRIGGER_NONE: break; } } } /** * Получение ссылки на объект контроллера */ $C = call_user_func(array($controller, 'getInstance'), $controller); /** * Проверка возможно ли вызвать метод котроллера * Если нет, выдаётся ошибка если надо */ if (!is_callable(array($C, $method))) { switch ($trigger) { case self::TRIGGER_404: trigger404(); break; case self::TRIGGER_ERROR: trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Method <b>%s</b> of controller <b>%s</b> cannot be called'), $method, $controller), E_USER_ERROR); default: case self::TRIGGER_NONE: break; } } /** * Вызов метода */ return call_user_func_array(array($C, $method), $arguments); }
/** * Совпадает ли поле с переданным значением * В случае ошибки возвращается 1-й аргумент, если он передан * Остальные игнорируются * * @param mixed $var * @param array $args * @return mixed */ private function match($var, $args) { $result = isset($args[0]) ? $var == $args[0] : FALSE; if ($result) { return TRUE; } else { return isset($args[1]) ? $args[1] : Open_Text::getInstance()->dget('validator', 'The field data does not match a given value'); } }
/** * Закрыть соединение с БД * Если параметр профиль не передан, то отключается подключение по умолчанию * * @param string $profile * @return bool Успех операции */ public function disconnect($profile = FALSE) { /** * Если профиль не передан, то берём профиль по умолчанию */ if ($profile === FALSE) { $profile = $this->getProfile(); } /** * Закрываем соединение и удаляем элемент текущего профиля из массива соединений */ if (in_array($profile, $this->links)) { mysql_close($this->links[$profile]) or triggerError("<i>" . Open_Text::getInstance()->dget('errors', 'Unable to close connection') . " - <b>" . $profile . "</b> !<br><b>#" . mysql_errno($this->links[$profile]) . "</b> - " . mysql_error($this->links[$profile]) . "</i><br />", E_DB); unset($this->links[$profile]); } return TRUE; }
/** * Получить все параметры из секции * * @param string $section Имя секции. Если секция не указана, берётся секция по умолчанию * @return mixed */ public function getAll($section = NULL) { /** * Если секция не указана, берём секцию по умолчанию */ if (!isset($section)) { $section = self::DEFAULT_CONFIG; } /** * Если такой секции не существует * То пытаемся подгрузить */ if (!isset($this->config[$section])) { $this->load($section, FALSE); } /** * Если секция существует после попытки загрузить, то возвращаем её целиком */ if (isset($this->config[$section])) { return $this->config[$section]; } /** * Если ничего не найдено, то выдаём ошибку */ trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Config section <b>%s</b> does not exist'), $section), E_USER_NOTICE); return FALSE; }
/** * Проверка поддерживается ли заданый формат методом * * @param int $supportedFormats Перечисленные через битовое ИЛИ поддерживаемые форматы * @param int $format Константа формата * @return bool */ private function checkSupportedFormat($supportedFormats, $format) { if (($supportedFormats & $format) !== $format) { trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Format <b>%s</b> is not supported'), $format), E_USER_ERROR); return FALSE; } return TRUE; }
/** * Проверить доступ роли $role к ресурсу $resource по действию $action * По умолчанию доступ запрещён * * @param array $role * @param array $resource * @param array $action * @return bool */ public function isAllowed($role, $resource, $action) { /** * Если роли, ресурса или действия не существует возвращается запрещение доступа и выдаётся ошибка */ if (!in_array($role, $this->roles)) { trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Unknown role <b>%s</b>'), $role), E_USER_WARNING); return FALSE; } else { if (!in_array($resource, array_keys($this->resources))) { trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Unknown resource <b>%s</b>'), $resource), E_USER_WARNING); return FALSE; } else { if (!in_array($action, $this->resources[$resource])) { trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Unknown action <b>%s</b>'), $action), E_USER_WARNING); return FALSE; } } } /** * Даём безоговорочный доступ роли с полным доступом (админу) */ if ($this->complete_access_role === $role) { return TRUE; } /** * Запрещение доступа по умолчанию */ $result = FALSE; /** * Проверка разрешения в массиве допуска * Массив допуска формируется с учётом всех родителей роли */ $allow = $this->getAllow($role); if (!in_array($resource, array_keys($allow))) { } else { if (!in_array($action, $allow[$resource])) { } else { $result = TRUE; } } return $result; }
/** * Заблокировать доступ к ресурсу с ключом $key на основе семафора * Эксклюзивная блокировка, т.е. семафор в данном случае является мьютексом * Желательно, чтобы ключ был целым значением от 0 до 255 * * @param string $key * @return bool Успех операции */ public function semLock($key) { /** * Если нет идентификатора */ if (!isset($this->semaphores[$key])) { /** * Пытаемся получить ключ к файлу и получить идентификатор семафора по ключу * В данном случае семафор является мьютексом */ if (($fkey = ftok(__FILE__, (int) $key & 0xff)) < 0 || ($this->semaphores[$key] = sem_get($fkey, 1, 0664, 1)) === FALSE) { /** * В случае ошибки при получении ключа в ftok() или при получении семафора sem_get() выдаётся стандартная ошибка */ trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Unable to get semaphore with key <b>%s</b>'), $key), E_USER_ERROR); } //p(strftime('%T', time()) .' '. numberFormat(fmod(microtime(true), 1), 4) ."\t $key sem got"); } /** * Пытаемся захватить семафор */ if (!($result = sem_acquire($this->identifiers[$name]))) { trigger_error(sprintf(Open_Text::getInstance()->dget('errors', 'Semaphore with key <b>%s</b> cannot be acquired'), $name), E_USER_WARNING); } //p(strftime('%T', time()) .' '. numberFormat(fmod(microtime(true), 1), 4) ."\t $name sem aquired"); return $result; }
/** * Пресекаем войну клонов */ public function __clone() { trigger_error(Open_Text::getInstance()->dget('errors', sprintf('Cannot clone %s', __CLASS__)), E_USER_ERROR); }
/** * Отобразить, либо сохранить в файл изображение * * @param int $type Тип сохраняемой картинки * @param string $filepath Путь к файлу, в который сохранить картинку, БЕЗ расширения * @param int $quality Качество изображения для PNG и JPG * @param int $filters Фильтры для PNG изображения */ public function output($type = IMAGETYPE_PNG, $filepath = NULL, $quality = NULL, $filters = NULL) { /** * Если не передано имя файла, то выводим картинку на экран */ if (isset($filepath)) { $filepath .= image_type_to_extension($type); } else { header('Content-type: ' . image_type_to_mime_type($type)); } switch ($type) { case IMAGETYPE_PNG: imagepng($this->resource, $filepath, $quality, $filters); break; case IMAGETYPE_JPEG: imagejpeg($this->resource, $filepath, $quality); break; case IMAGETYPE_GIF: imagegif($this->resource, $filepath); break; default: trigger_error(Open_Text::getInstance()->dget('errors', 'Wrong or unsupported image type specified for output image'), E_USER_ERROR); break; } }
/** * Рисует картинку и возвращает её в виде image/png * */ public function draw($isRedraw = false) { $C = $this->config; $I = $this->input; $V = $this->view; $S = Open_Session::getInstance(); $M = getModel('CaptchaModel'); /** * Получаем параметры из конфига */ $config = $C->get('captcha', array('angle', 'background', 'color', 'distraction', 'font', 'height', 'length', 'size', 'sessionKey', 'width')); $angle =& $config['angle']; $background =& $config['background']; $color =& $config['color']; $distraction =& $config['distraction']; $font = FONTS_PATH . $config['font']; $height =& $config['height']; $length =& $config['length']; $size =& $config['size']; $sessionKey =& $config['sessionKey']; $width =& $config['width']; /** * Получить строку для прорисовки */ $string = $M->get($isRedraw); /** * Создание картинки */ if (!($img = imagecreatetruecolor($width, $height))) { trigger_error(Open_Text::getInstance()->dget('errors', 'New image cannot be initialized for captcha'), E_USER_ERROR); } /** * Формирование цвета фона */ $background = $this->getColor($img, $background); imagefill($img, 0, 0, $background); /** * Создание надписи на картинке */ for ($i = 0; $i < $length; $i++) { $currentSize = $size + mt_rand(-(int) ($size / 6), (int) ($size / 6)); $x = ($width - 20) / $length * $i + mt_rand(11, 13); $y = ($height + $currentSize) / 2 + mt_rand(-4, 4); $char = mb_substr($string, $i, 1); /** * Отвлекающий символ */ imagettftext($img, $currentSize + (int) ($size / 8), mt_rand(-$angle, $angle), $x + mt_rand(-4, 4), $y + mt_rand(-4, 4), $this->getColor($img, $distraction), $font, $char); /** * Основной цвет */ imagettftext($img, $currentSize, mt_rand(-$angle, $angle), $x, $y, $this->getColor($img, $color), $font, $char); /** * Повторение надписи цветом фона для создания пересечений */ imagettftext($img, $currentSize + (int) ($size / 12), mt_rand(-$angle, $angle), $x + mt_rand(-2, 2), $y + mt_rand(-2, 2), $background, $font, $char); } /** * Установка header-заголовков * Отменяем кеширование картинки и задаём тип содержимого */ $V->setHeaders(array("Expires: Mon, 26 Jul 1997 05:00:00 GMT", "Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT", "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0", "Pragma: no-cache", "Content-type: image/png")); $V->sendHeaders(); /** * Выдача картинки */ imagepng($img); imagedestroy($img); }