/** @return PsProfilerInterface */ private function getProfiler($profilerId) { //Не будем выполнять get_file_name($profilerId), так как нужно вернуть профайлер максимально быстро if ($this->CAHCE->has($profilerId)) { return $this->CAHCE->get($profilerId); } check_condition($profilerId, 'Profiler id cannot be empty.'); if (!$this->enabled) { return $this->CAHCE->set($profilerId, new PsProfilerEmpty($profilerId)); } $di = $this->getProfilerDi($profilerId); $pr = null; //Проверим текущий размер профайлера if ($di->isMaxSize(ConfigIni::profilingMaxFileSize())) { $locked = PsLock::lock(__CLASS__ . "::compressProfiler({$profilerId})", false); if ($locked) { $this->compressProfiler($di); PsLock::unlock(); } else { //Разимер превышен и мы не смогли получить лок для дампа профайлера. Не будем в этот раз вести профайлинг. $pr = new PsProfilerEmpty($profilerId); } } return $this->CAHCE->set($profilerId, $pr ? $pr : new PsProfilerImpl($profilerId)); }
private function doResizeImpl(DirItem $srcDi, $w, $h) { $dim = $w . 'x' . $h; $cacheKey = md5("[{$srcDi}]:[{$dim}]"); if ($this->CAHCE->has($cacheKey)) { return $this->CAHCE->get($cacheKey); } $dstDi = DirManager::autogen("images/{$dim}")->cdToHashFolder(null, null, $cacheKey)->getDirItem(null, $cacheKey, SYSTEM_IMG_TYPE); if ($dstDi->isImg()) { return $this->CAHCE->set($cacheKey, $dstDi); } if (!$srcDi->isImg()) { return $this->CAHCE->set($cacheKey, null); } PsLock::lockMethod(__CLASS__, __FUNCTION__); try { if (!$dstDi->isImg()) { //Картинка не была пересоздана в другом потоке SimpleImage::inst()->load($srcDi)->resizeSmart($w, $h)->save($dstDi, SYSTEM_IMG_TYPE)->close(); } } catch (Exception $ex) { PsLock::unlock(); throw $ex; } PsLock::unlock(); return $this->CAHCE->set($cacheKey, $dstDi); }
/** * Метод возвращает формы слова во всех падежах * * @param type $word * @return array - все склонения слова в виде массива, где под индексом 0 - оригинальное значение */ public function getInflections($word) { $word = PsCheck::notEmptyString(trim($word)); if ($this->CACHE->has($word)) { return $this->CACHE->get($word); } $this->LOGGER->info(); $this->LOGGER->info('> Запрошено склонение для слова [{}]', $word); //$fileName = iconv('UTF-8', 'cp1251', $word); /* * Ищем в БД */ $inflections = InflectsBean::inst()->getInflections($word); if (is_array($inflections)) { $this->LOGGER->info('< Cклонение для [{}] найдено в базе: {}', $word, array_to_string($inflections)); return $this->CACHE->set($word, $inflections); } /* * Загружаем с сервиса */ $inflections = $this->loadInflectionImpl($word); if (is_array($inflections) && count($inflections) == 7) { $this->LOGGER->info('< Склонение для [{}] успешно загружено: {}', $word, array_to_string($inflections)); //Не забудем сохранить полеченное склонение для слова InflectsBean::inst()->saveInflections($inflections); return $this->CACHE->set($word, $inflections); } /* * Загрузить не удалось, возвращаем балванку */ $inflections = array_fill(0, 7, $word); $this->LOGGER->info('< Склонение для [{}] не определено, возвращаем "болванку": {}', $word, array_to_string($inflections)); return $this->CACHE->set($word, $inflections); }
/** * Создание экземпляра класса для сущности фолдинга * @return FoldedClass */ public function getEntityClassInst($ident, $cache = true) { if (!$cache || !$this->INSTS_CACHE->has($ident)) { //Получим элемент - класс $php = $this->getResourceDi($ident, self::RTYPE_PHP); //Проверим, что это - файл check_condition($php->isFile(), 'Не найден класс реализации для сущности ' . $this->getTextDescr($ident)); //Проверим сущность на изменение $this->checkEntityChanged($ident); //Получим FoldedEntity, так как её потом нужно будет передать в конструктор $foldedEntity = $this->getFoldedEntity($ident); //Подключим класс, не будем заставлять трудиться класслоадер require_once $php->getAbsPath(); //Построим название класса на основе идентификатора сущности $baseFoldedClass = 'FoldedClass'; $class = $this->ident2className($ident); check_condition(PsUtil::isInstanceOf($class, $baseFoldedClass), "Класс для сущности {$foldedEntity} не является наследником {$baseFoldedClass}"); //Создаём акземпляр $inst = new $class($foldedEntity); //Отлогируем $this->LOGGER->info("Instance of {$class} created."); FoldedResourcesManager::onEntityAction(FoldedResourcesManager::ACTION_ENTITY_INST_CREATED, $this, $ident); return $cache ? $this->INSTS_CACHE->set($ident, $inst) : $inst; } return $this->INSTS_CACHE->get($ident); }
/** * Основной метод, выполняющий построение контроллера для просмотра постов. * Контроллер может быть отображен в двух случаях: * 1. На странице с просмотром всех постов * 2. На странице с рубрикой * * @return ShowcasesControllerPanel */ private function getScPanel($postType, Rubric $rubric = null) { $key = $postType . '-' . ($rubric ? $rubric->getIdent() : ''); if (!$this->CACHE->has($key)) { $plugins[] = $this->getBaseControllerIdents(); if ($rubric) { $plugins[] = Mappings::RUBRIC_2_SCCONTROLLERS($postType)->getMappedEntitys($rubric->getIdent()); } $insts = $this->getUserAcessibleClassInsts(to_array_expand($plugins)); $ctxt = new ShowcasesControllerCtxt($rubric); $result = array(); /** @var ShowcasesControllerItem */ foreach ($insts as $ident => $inst) { $inst->doProcess($ctxt); $result[$ident] = $inst; } $this->CACHE->set($key, new ShowcasesControllerPanel($result)); } return $this->CACHE->get($key); }
/** @return DirItem */ public final function getUploadedFileDi($uploadId, $userId = null) { if (!$this->CACHE->has($uploadId)) { $this->assertCanUseDb(__FUNCTION__); $this->assertAutonomous('файл не может быть загружен из БД'); $file = UploadsBean::inst()->getFile($this->DBTYPE, $uploadId, $userId); $this->LOGGER->info("File [{$uploadId}] loaded from DB for user [{$userId}] ? {}.", var_export($file, true)); $this->CACHE->set($uploadId, is_array($file) && array_key_exists('name', $file) ? DirItem::inst($file['name']) : null); } return $this->CACHE->get($uploadId); }
/** * Метод возвращает DirItem элемента, содержащего картинку-представление формулы. * * @param type $formula - текстовая формула * @param type $createIfNotExists - признак, стоит ли пытаться создавать картинку для формулы * @return DirItem */ public function getImgDi($formula, $createIfNotExists = true) { $formula = TexTools::safeFormula($formula); if ($this->CACHE->has($formula)) { return $this->CACHE->get($formula); } $hash = TexTools::formulaHash($formula); $imgDI = $this->DM->getHashedDirItem(null, $hash, $hash, 'gif'); if ($imgDI->isImg()) { $this->CACHE->set($formula, $imgDI); return $imgDI; } if (!$createIfNotExists) { return null; } //Создаём структуру директорий $imgDI->makePath(); $contents = ''; //Запрашиваем графическое представление $this->PROFILER->start($formula); try { //TODO - делать это локально, чтобы не зависить от стороннего сервиса $handle = fopen('http://latex.codecogs.com/gif.latex?' . rawurlencode($formula), 'r'); while (!feof($handle)) { $contents .= fread($handle, 8192); } fclose($handle); $this->PROFILER->stop(); } catch (Exception $ex) { //Останавливаем профайлер без сохранения $this->PROFILER->stop(false); //Делаем дамп ошибки ExceptionHandler::dumpError($ex, "Tex formula convertation requested for:\n{$formula}"); //Попытаемся подчистить за собой, если мы что-то создали $imgDI->remove(); //Пробрасываем эксепшн throw $ex; } //Сохраняем картинку в файл $imgDI->putToFile($contents); //Сохраним текстовое представление $this->DM->getHashedDirItem(null, $hash, $hash, 'gif.tex')->putToFile($formula); $this->CACHE->set($formula, $imgDI); return $imgDI; }
/** * Метод возвращает сущность фолдинга по заданному коду * * @return FoldedEntity Сущность, соответствующая заданному коду */ public function getFoldedEntityByDbCode($code) { return $this->CACHE->has($code) ? $this->CACHE->get($code) : $this->CACHE->set($code, FoldedStorageInsts::getFoldedEntityByUnique(FoldingBean::inst()->getUniqueByCode($code))); }
public function getFromCache($id, $group, $REQUIRED_KEYS = null, $sign = null) { $cacheId = $this->localCacheKey($id, $group); //Сначала ищем в локальном хранилище if ($this->CACHE->has($cacheId)) { $CACHED = $this->CACHE->get($cacheId); if ($CACHED['sign'] == $sign) { $this->LOGGER->info("Информация по ключу '{$cacheId}' найдена в локальном кеше"); return $CACHED['data']; } else { $this->LOGGER->info("Информация по ключу '{$cacheId}' найдена в локальном кеше, но старая и новая подписи не совпадают: [{}]!=[{}]. Чистим...", $CACHED['sign'], $sign); $this->CACHE->remove($cacheId); $this->CACHELITE->remove($id, $group); return null; } } /* * Самое интересное и спорное место всей реализации. * Нам нужно отслеживать свежесть кешей. Сами по себе они сбрасываются через опередённое время (время жизни кеша). * Но это время достаточно велико и, если мы сейчас, например, правим код, то нам некогда ждать, пока всё само сабой обновится. * * Кеши валидируются через свои, so called, "подписи". * Например - структура проекта (строка навигации) зависит от кол-ва постов в каждом разделе, но при этом если какой-либо пост изменится, то * на подпись кеша для навигации это никак не повлияет, а ведь в посте мог измениться анонс, например. * * Всё крутится вокруг изменения сущностей фолдингов. При изменении сущности будет сброшен кеш, который от этого фолдинга зависит. * Весь вопрос в том - как отслеживать эти изменения?.. Единственное решение - пробегать по всем фолдингам и выполнять checkAllEntityChanged. * Решение это довольно дорогостоящее. Даже если сущность не изменилась и кеш не будет перестроен, мы вынуждены выполнить очень много действий. * * Есть два варинта для обеспечения "свежести" кешей: * * Вариант №1. * Каждый раз при запросе кеша выполнять checkAllEntityChanged для фолдингов, от которых зависит запрашеваемая группа кешей. * Это нам ВСЕГДА обеспечит свежесть всех кешей, но данная операция является довольно тяжёлой. * * Вариант №2. * Выполнять проверку checkAllEntityChanged для всех фолдингов, но так как это довольно дорого - делать это не каждый раз, а с определённой периодичностью. * Это также обеспечивает свежесть кешей, но операция - очень тяжёлая, поэтому её нельзя выполнять каждый раз. * В идеале её вообще должен выполнять внешний процесс, запускаемый раз в EXTERNAL_PROCESS_CALL_DELAY минут. * Будем эмулировать его работу посредством класса ExternalProcess. * В продакшене будет работать второй вариант - там частота обновлений кешей не так важна. */ $this->validateGroup($group); PsProfiler::inst(__CLASS__)->start('LOAD from cache'); $CACHED = $this->CACHELITE->get($id, $group); PsProfiler::inst(__CLASS__)->stop(); if (!$CACHED) { $this->LOGGER->info("Информация по ключу '{$cacheId}' не найдена в кеше"); return null; } if (!is_array($CACHED)) { $this->LOGGER->info("Информация по ключу '{$cacheId}' найдена в хранилище, но не является массивом. Чистим..."); $this->CACHELITE->remove($id, $group); return null; } if (!array_has_all_keys(array('data', 'sign'), $CACHED)) { $this->LOGGER->info("Информация по ключу '{$cacheId}' найдена в хранилище, но отсутствует параметр sign или data. Чистим..."); $this->CACHELITE->remove($id, $group); return null; } if ($CACHED['sign'] != $sign) { $this->LOGGER->info("Информация по ключу '{$cacheId}' найдена в хранилище, но старая и новая подписи не совпадают: [{}]!=[{}]. Чистим...", $CACHED['sign'], $sign); $this->CACHELITE->remove($id, $group); return null; } $MUST_BE_ARRAY = is_array($REQUIRED_KEYS); $REQUIRED_KEYS = to_array($REQUIRED_KEYS); if ($MUST_BE_ARRAY || !empty($REQUIRED_KEYS)) { //Если нам переданы ключи для проверки, значит необходимо убедиться, что сами данные являются массивом if (!is_array($CACHED['data'])) { $this->LOGGER->info("Информация по ключу '{$cacheId}' найдена в хранилище, но не является массивом. Чистим..."); $this->CACHELITE->remove($id, $group); return null; } foreach ($REQUIRED_KEYS as $key) { if (!array_key_exists($key, $CACHED['data'])) { $this->LOGGER->info("Информация по ключу '{$cacheId}' найдена, но в данных отсутствует обязательный ключ [{$key}]. Чистим..."); $this->CACHELITE->remove($id, $group); return null; } } } $this->LOGGER->info("Информация по ключу '{$cacheId}' найдена в хранилище"); //Перенесём данные в локальный кеш для быстрого доступа return array_get_value('data', $this->CACHE->set($cacheId, $CACHED)); }