/** * Метод разбирает файл с ребусами, который выглядит как: * * ребус 1 * ответ 1.1 * ответ 1.2 * * ребус 2 * ответ 2.1 * ответ 2.2 * * @return type */ private function parseAnswersFile(DirItem $di) { $result = array(); $lines = explode("\n", trim($di->getFileContents(false))); $current = null; foreach ($lines as $line) { $line = trim($line); if ($line && !$current) { //Начинается новый ребус $current = $line; $result[$current] = array(); } else { if ($line && $current) { //Ответ на ребус $result[$current][] = $line; } else { if (!$line && $current) { //Пробел, закончили ребус $current = null; } } } } if ($this->LOGGER->isEnabled()) { $this->LOGGER->info('Rebuses of {}:', $di->getRelPath()); $this->LOGGER->info(print_r($result, true)); $this->LOGGER->info(); } return $result; }
/** * Функция производит финализацию страницы, показываемой пользователю, добавляя к ней элемент с содержащимися в нём ссылками. */ public function appendLibItemsToPageContent($CONTENT) { $matches = array(); $data = PsConstJs::BUBBLE_LOADER_FOLDING_DATA; $pattern = "/data-{$data}=\"(.+?)\"/si"; preg_match_all($pattern, $CONTENT, $matches); $matches = array_unique(array_get_value(1, $matches, array())); $items = array(); foreach ($matches as $unique) { $entity = Handlers::getInstance()->getFoldedEntityByUnique($unique, false); if ($entity && $entity->getFolding()->isItByType(LibResources::LIB_FOLDING_TYPE)) { $items[$entity->getUnique()] = $entity->getFolding()->getBubble($entity->getIdent()); } } $has = count($items) > 0; if ($this->LOGGER->isEnabled()) { $this->LOGGER->info(); $this->LOGGER->info('Библиотечные элементы, добавленные на страницу:'); if ($has) { foreach ($items as $unique => $lib) { $this->LOGGER->info("\t{$unique}"); } } else { $this->LOGGER->info("\t-- Нет --"); } } return $CONTENT . ($has ? PsHtml::div(array('id' => PsConstJs::BUBBLE_LOADER_FOLDING_STORE_ID), implode('', $items)) : ''); }
protected function __construct() { $this->CACHE = new SimpleDataCache(); $this->LOGGER = PsLogger::inst(__CLASS__); if ($this->LOGGER->isEnabled()) { PsShotdownSdk::registerDestructable($this, PsShotdownSdk::FoldedResourcesManager); } }
/** * Метод регистрации экземпляров фолдингов * * @param FoldedResources $inst - экземпляр */ protected final function register(FoldedResources $inst) { $unique = $inst->getUnique(); if (array_key_exists($unique, $this->FOLDINGS)) { PsUtil::raise('Folding \'{}\' is already registered. Cannot register \'{}\' with same unique.', $this->FOLDINGS[$unique], $inst); } else { $this->FOLDINGS[$unique] = $inst; if ($this->LOGGER->isEnabled()) { $this->LOGGER->info('+{}. {}, count: {}.', pad_left(count($this->FOLDINGS), 3, ' '), $inst, FoldedStorage::getEntitiesCount($unique)); } } }
/** * Метод выполняет фактическую замену TEX тега на его представление в виде картинки или спрайта. * * @param str $original \[v_{\text{cp}}=\frac{\Delta S}{\Delta t}\] * @param str $formula v_{\text{cp}}=\frac{\Delta S}{\Delta t} * @param str $type block or inline */ public function _replaceInTextImpl($original, $formula, $isBlock) { $type = $isBlock ? 'block' : 'inline'; $replace = ''; if ($formula) { /* * Проверяем, разрешена ли замена формул на спрайты и есть ли сейчас контекст. * Если всё выполнено, то это вовсе не означает, что необходимый спрайт подключен и есть * спрайт для данной формулы. */ if (PsDefines::isReplaceFormulesWithSprites() && FoldedContextWatcher::getInstance()->getSpritableContext()) { $replace = CssSpritesManager::getFormulaSprite(FoldedContextWatcher::getInstance()->getSpritableContext()->getSpritable(), $formula, array('TeX', $type)); } /* * Если спрайта не нашлось, то заменяем на картинку. */ if (!$replace) { $imgDi = $this->getImgDi($formula); $imgPath = $imgDi ? $imgDi->getRelPath() : null; if ($imgPath) { $replace = PsHtml::img(array('src' => $imgPath, 'class' => "TeX {$type}", 'alt' => '')); } else { $replace = $original; } } } /* * Логирование */ if ($this->LOGGER->isEnabled()) { $this->LOGGER->info(++$this->replaced . ". Replacing {$type} TeX"); $this->LOGGER->info("FULL: {$original}"); $this->LOGGER->info("CONTENT: {$formula}"); $this->LOGGER->info("SAVED: {$formula}"); $this->LOGGER->info("REPLACED: {$replace}"); $this->LOGGER->info("\n"); } return $replace; }
/** * Копирует ячейки из картинки-источника в картинку-мозайку. * * @param array $INFO - информация о картинке * @param array $cells - ячейки из БД, которые будут скопированы * @param type $userId - код пользователя, к которому ячейки будут привязаны */ private function copyCells(array $cells, $userId = null) { //Проверим, есть ли сейчас "чистая" картинка, на которую мы будем копировать ячейки if (!$this->diDst()->isImg()) { SimpleImage::inst()->create($this->width, $this->height, self::BG_COLOR)->save($this->diDst())->close(); } if (empty($cells)) { return; //--- } if ($this->LOGGER->isEnabled()) { $this->LOGGER->info('Copy cells of image ' . $this->id . ', user for bind: ' . var_export($userId, true)); $this->LOGGER->info('Not processed cells: ' . print_r($cells, true)); } PsUtil::startUnlimitedMode(); $this->PROFILER->start('Copy cells of img ' . $this->id); //1. Разберёмся с ячейками - привяжем к пользователю те их них, которые никому не принадлежат foreach ($cells as $cell) { $n_part = 1 * $cell['n_part']; $owned = !!$cell['owned']; //Ячейка должна кому-то принадлежать, либо быть привязана к переданному пользователю check_condition($owned || is_numeric($userId), "Ячейка {$this->id}.{$n_part} никому не принадлежит."); if (!$owned) { //Если ячейка уже привязана к пользователю, то не будем лишний раз дёргать базу $this->LOGGER->info('{}. Cell binded to user {}', $n_part, $userId); $this->BEAN->markAsOwned($userId, $this->id, $n_part); } } //2. Копируем ячейки, предварительно объединив их $srcImg = SimpleImage::inst()->load($this->diSrc()); $dstImg = SimpleImage::inst()->load($this->diDst()); $unioned = MosaicImageCellsCompositor::union($cells, $this->cellWidth, $this->cellHeight, false); foreach ($unioned as $cell) { $x1 = $cell[0]; $y1 = $cell[1]; $x2 = $cell[2]; $y2 = $cell[3]; $w = $x2 - $x1; $h = $y2 - $y1; $dstImg->copyFromAnother($srcImg, $x1, $y1, $x1, $y1, $w, $h)->save(); $this->LOGGER->info('Copied rectangle: {}x{}-{}x{}', $x1, $y1, $x2, $y2); } $srcImg->close(); $dstImg->close(); $this->PROFILER->stop(); }
/** * Распечатывает текущее состояние дерева зависимости в лог. * При этом в дереве отмечаются всегда только полностью проверенные сущности. * Если, например, группа зависит от таблиц БД, которые были провалидированы, но при этом * сама группа отмечена, как валидная, то мы увидим подобную картину: * +GALLERY: | Сущности базы: | ps_gallery | ps_gallery_images */ private function logTrees($caption, $doLog = true) { if (!$doLog || !$this->LOGGER->isEnabled()) { return; //--- } $this->LOGGER->infoBox($caption); $GROUPS = PSCache::getCacheGroups(); $hasPrintedGroups = false; foreach ($GROUPS as $group) { $isGroupPrinted = false; foreach ($this->TREE_DEP as $type => $groups2entitys) { if (!array_key_exists($group, $groups2entitys)) { continue; } $valGr = !$this->isGroupValidatable($group); $valType = !$valGr && !$this->isTypeValidateble($type); if ($hasPrintedGroups && !$isGroupPrinted) { $this->LOGGER->info(); } if (!$isGroupPrinted) { $isGroupPrinted = true; $hasPrintedGroups = true; $this->LOGGER->info("\t" . ($valGr ? '+' : '|') . $group . ':'); } else { $this->LOGGER->info("\t|"); } $this->LOGGER->info("\t" . ($valType ? '+' : '|') . "\t{$type}:"); foreach ($groups2entitys[$group] as $child) { $varChild = !$valGr && !$valType && !in_array($child, $this->getChildsForValidate($type, $group)); $this->LOGGER->info("\t" . ($varChild ? '+' : '|') . "\t\t{$child}"); } } } $this->LOGGER->info(pad_left('', ps_strlen($caption) + 4, '+')); $this->LOGGER->info(); }
/** * В конструкторе пробежимся по всем хранилищам и соберём все фолдинги */ protected function __construct() { $this->LOGGER = PsLogger::inst(__CLASS__); $this->PROFILER = PsProfiler::inst(__CLASS__); $this->PROFILER->start('Loading folding entities'); /* * Пробегаемся по всему, настроенному в foldings.ini */ foreach (FoldingsIni::foldingsRel() as $foldedUnique => $dirRelPathes) { $this->FOLDING_2_ENTITY_2_ENTABSPATH[$foldedUnique] = array(); $this->FOLDING_2_ENTITY_2_ENTRELPATH[$foldedUnique] = array(); /* * Загрузим карту сущностей */ foreach (array_unique($dirRelPathes) as $dirRelPath) { $dm = DirManager::inst($dirRelPath); foreach ($dm->getSubDirNames() as $entity) { //Не будем проверять наличие этой сущности, более поздние смогут её переопределить //array_key_exists($entity, $this->FOLDING_2_ENTITY_2_ENTABSPATH[$foldedUnique]) $this->FOLDING_2_ENTITY_2_ENTABSPATH[$foldedUnique][$entity] = $dm->absDirPath($entity); $this->FOLDING_2_ENTITY_2_ENTRELPATH[$foldedUnique][$entity] = $dm->relDirPath($entity); } } ksort($this->FOLDING_2_ENTITY_2_ENTABSPATH[$foldedUnique]); ksort($this->FOLDING_2_ENTITY_2_ENTRELPATH[$foldedUnique]); /* * Построим карты сущностей к типам фолдингов, чтобы мы могли через них выйти на фолдинг */ self::extractFoldedTypeAndSubtype($foldedUnique, $ftype, $fsubtype); /* * Построим карту отношения идентификатора фолдинга к коду ресурса * slib => lib-s */ $this->SOURCE_2_FOLDING[$fsubtype . $ftype] = $foldedUnique; /* * Построим карту отношения идентификатора фолдинга к префиксу класса * SLIB_ => lib-s */ $this->CLASSPREFIX_2_FOLDING[strtoupper($fsubtype . $ftype) . '_'] = $foldedUnique; /* * Построим карту отношения типа фолдинга к массиву подтипов фолдингов * lib => array('s', 'p') * pl = > null */ if (array_key_exists($ftype, $this->TYPE_2_STYPE)) { //Если мы второй раз попали в этот блок для типа фолдинга, то он должен иметь подтип [lib=>array('s')]. check_condition(is_array($this->TYPE_2_STYPE[$ftype]), "Уже зарегистрирован фолдинг с типом [{$ftype}] без подтипов"); $this->TYPE_2_STYPE[$ftype][] = check_condition($fsubtype, "Уже зарегистрирован фолдинг с типом [{$ftype}] и с подтипами"); } else { if ($fsubtype) { //Новый тип фолдинга с подтипом. $this->TYPE_2_STYPE[$ftype] = array($fsubtype); } else { //Новый тип фолдинга без подтипа. $this->TYPE_2_STYPE[$ftype] = null; } } } //Отсортируем по уникальным кодам фолдингов ksort($this->FOLDING_2_ENTITY_2_ENTABSPATH); ksort($this->FOLDING_2_ENTITY_2_ENTRELPATH); ksort($this->TYPE_2_STYPE); //Установим идентификаторы фолдингов $this->FOLDINGS = array_keys($this->FOLDING_2_ENTITY_2_ENTRELPATH); $sec = $this->PROFILER->stop(); if ($this->LOGGER->isEnabled()) { $this->LOGGER->info('FOLDINGS: {}', print_r($this->FOLDINGS, true)); $this->LOGGER->info('FOLDING_2_ENTITY_2_ENTABSPATH: {}', print_r($this->FOLDING_2_ENTITY_2_ENTABSPATH, true)); $this->LOGGER->info('FOLDING_2_ENTITY_2_ENTRELPATH: {}', print_r($this->FOLDING_2_ENTITY_2_ENTRELPATH, true)); $this->LOGGER->info('TYPE_2_STYPE: {}', print_r($this->TYPE_2_STYPE, true)); $this->LOGGER->info('CLASSPREFIX_2_FOLDING: {}', print_r($this->CLASSPREFIX_2_FOLDING, true)); $this->LOGGER->info('SOURCE_2_FOLDING: {}', print_r($this->SOURCE_2_FOLDING, true)); $this->LOGGER->info('BUILDING_TIME: {} sec', $sec->getTotalTime()); } }
private function uploadFileImpl(DirItem $source, FILEAdapter $file = null, $userId = null, array $params = array()) { $userId = $this->checkUserId($userId); $this->LOGGER->info("Processing file upload for user [{$userId}], source {$source}."); $aa = ArrayAdapter::inst($params); $uploaded = $file ? $source : null; $originalName = $file ? $file->getOriginalName() : $source->getName(); $dbMsg = null; try { $this->LOGGER->info('Calling onBeforeSave...'); $dbMsg = $this->onBeforeSave($source, $userId, $aa); $this->LOGGER->info("\tDone!"); } catch (Exception $ex) { $this->LOGGER->info('Error occurred in onBeforeSave method: ' . $ex->getMessage()); $this->LOGGER->info('Source file will be deleted ? {}.', var_export(!!$uploaded, true)); if ($uploaded) { $uploaded->remove(); } throw $ex; } if ($uploaded) { //Это уже и так загруженный файл $this->LOGGER->info('Source file is uploaded file'); } else { $this->LOGGER->info('Move source file to uploads dir'); $uploaded = $this->makeTmpDirItem(); $source->copyTo($uploaded); } if ($this->LOGGER->isEnabled()) { $this->LOGGER->info("\tUploaded file: {$uploaded}"); $this->LOGGER->info("\tOriginal name: [{$originalName}]"); $this->LOGGER->info("\tMime: [{$uploaded->getMime()}]"); $this->LOGGER->info("\tParams: " . array_to_string($params, false)); } $uploadId = null; if ($this->isStoreToDb()) { $this->LOGGER->info("Saving upload file into database. DbMsg: '{$dbMsg}'."); try { $uploadId = UploadsBean::inst()->saveFileUpload($this->DBTYPE, $uploaded->getAbsPath(), $originalName, $uploaded->getMime(), $userId, $dbMsg); //Почистим кеш, вдруг мы запрашивали информацию по данному файлу $this->CACHE->remove($uploadId); $this->LOGGER->info("\tFile successfully saved, uploadId = {$uploadId}."); } catch (Exception $ex) { $this->LOGGER->info('Error occured while saving file to DB: ' . $ex->getMessage()); $this->LOGGER->info('Deleting upload file...'); $uploaded->remove(); $uploaded = null; throw $ex; } $uploaded->setData('id', $uploadId); } try { $this->LOGGER->info('Calling onAfterSave...'); $this->onAfterSave($uploaded, $userId, $aa); $this->LOGGER->info("\tDone!"); } catch (Exception $ex) { $this->LOGGER->info('Error occured in onAfterSave method: ' . $ex->getMessage()); if (is_inumeric($uploadId)) { $this->LOGGER->info('Deleting db record...'); UploadsBean::inst()->clearFileUpload($uploadId); $uploadId = null; } $this->LOGGER->info('Deleting upload file...'); $uploaded->remove(); $uploaded = null; throw $ex; } /* * Если класс работает автономно и не работает с базой, то файл нужно удалить. */ if ($this->isAutonomous() && !$this->isStoreToDb()) { $this->LOGGER->info('Class is auto clean, deleting uploaded file...'); $uploaded->remove(); $uploaded = null; } $this->LOGGER->info(''); return $uploaded; }
public function fetchTplImpl($ident, $smParams = null, $returnType = self::FETCH_RETURN_CONTENT, $addResources = false, $cacheId = null) { $this->assertHasAccess($ident); $logMsg = null; if ($this->LOGGER->isEnabled()) { $rqNum = ++self::$FETCH_REQUEST_CNT; $logMsg = "#{$rqNum} Smarty params count: " . count(to_array($smParams)) . ", type: {$returnType}, resources: " . var_export($addResources, true) . ", " . ($cacheId ? "cache id: [{$cacheId}]" : 'nocache'); $this->LOGGER->info("Tpl fetching requested for entity [{$ident}]. {$logMsg}"); FoldedResourcesManager::onEntityAction(FoldedResourcesManager::ACTION_ENTITY_FETCH_REQUESTD, $this, $ident, $logMsg); } $entity = $this->getFoldedEntity($ident); //Сразу установим зависимость от текущей сущности FoldedContextWatcher::getInstance()->setDependsOnEntity($entity); $CTXT = $this->getFoldedContext(); $PCLASS = $CTXT->tplFetchParamsClass(); $PCLASS_BASE = FoldedTplFetchPrams::getClassName(); check_condition(PsUtil::isInstanceOf($PCLASS, $PCLASS_BASE), "Класс [{$PCLASS}] для хранения данных контекста {$CTXT} должен быть подклассом {$PCLASS_BASE}"); //Если мы не возвращаем содержимое, то в любом случае ресурсы добавлять не к чему $addResources = $addResources && !in_array($returnType, array(self::FETCH_RETURN_PARAMS, self::FETCH_RETURN_PARAMS_OB)); $keysRequired = PsUtil::getClassConsts($PCLASS, 'PARAM_'); $keysRequiredParams = array_diff($keysRequired, array(FoldedTplFetchPrams::PARAM_CONTENT)); $PARAMS = null; $PARAMS_KEY = null; $CONTENT = null; $CONTENT_KEY = null; $RETURN_KEY = null; if ($cacheId) { $cacheId = ensure_wrapped_with($cacheId, '[', ']') . '[' . PsDefines::getReplaceFormulesType() . ']'; $RETURN_KEY = $cacheId . '-' . $returnType; if (array_key_exists($ident, $this->FETCH_RETURNS)) { if (array_key_exists($RETURN_KEY, $this->FETCH_RETURNS[$ident])) { return $this->FETCH_RETURNS[$ident][$RETURN_KEY]; } } else { $this->FETCH_RETURNS[$ident] = array(); } $PARAMS_KEY = empty($keysRequiredParams) ? null : $cacheId . '-params'; $CONTENT_KEY = $cacheId . '-content'; switch ($returnType) { case self::FETCH_RETURN_FULL: case self::FETCH_RETURN_FULL_OB: $CONTENT = $this->getFromFoldedCache($ident, $CONTENT_KEY); $PARAMS = $PARAMS_KEY ? $this->getFromFoldedCache($ident, $PARAMS_KEY, $keysRequiredParams) : array(); if ($CONTENT && is_array($PARAMS)) { $CONTENT = $addResources ? $this->getResourcesLinks($ident, $CONTENT) : $CONTENT; $PARAMS[FoldedTplFetchPrams::PARAM_CONTENT] = $CONTENT; switch ($returnType) { case self::FETCH_RETURN_FULL: return $this->FETCH_RETURNS[$ident][$RETURN_KEY] = $PARAMS; case self::FETCH_RETURN_FULL_OB: return $this->FETCH_RETURNS[$ident][$RETURN_KEY] = new $PCLASS($PARAMS); default: raise_error("Unprocessed fetch return type [{$returnType}]."); } } break; case self::FETCH_RETURN_CONTENT: $CONTENT = $this->getFromFoldedCache($ident, $CONTENT_KEY); if ($CONTENT) { $CONTENT = $addResources ? $this->getResourcesLinks($ident, $CONTENT) : $CONTENT; return $this->FETCH_RETURNS[$ident][$RETURN_KEY] = $CONTENT; } break; case self::FETCH_RETURN_PARAMS: case self::FETCH_RETURN_PARAMS_OB: $PARAMS = $PARAMS_KEY ? $this->getFromFoldedCache($ident, $PARAMS_KEY, $keysRequiredParams) : array(); if (is_array($PARAMS)) { switch ($returnType) { case self::FETCH_RETURN_PARAMS: return $this->FETCH_RETURNS[$ident][$RETURN_KEY] = $PARAMS; case self::FETCH_RETURN_PARAMS_OB: return $this->FETCH_RETURNS[$ident][$RETURN_KEY] = new $PCLASS($PARAMS); default: raise_error("Unprocessed fetch return type [{$returnType}]."); } } break; } } $settedNow = false; if (!$entity->equalTo(FoldedContextWatcher::getInstance()->getFoldedEntity())) { $CTXT->setContextWithFoldedEntity($entity); $settedNow = true; } try { $CONTENT = $this->getTpl($ident, $smParams)->fetch(); $entityNow = FoldedContextWatcher::getInstance()->getFoldedEntity(); check_condition($entity->equalTo($entityNow), "After tpl fetching folded entity [{$entity}] chenged to [{$entityNow}]"); $PARAMS_FULL = $CTXT->finalizeTplContent($CONTENT); check_condition(is_array($PARAMS_FULL), "After [{$entity}] tpl finalisation not array is returned"); $keysReturned = array_keys($PARAMS_FULL); if (count(array_diff($keysReturned, $keysRequired)) || count(array_diff($keysRequired, $keysReturned))) { raise_error("After [{$entity}] tpl finalisation required keys: " . array_to_string($keysRequired) . '], returned keys: [' . array_to_string($keysReturned) . ']'); } if ($this->LOGGER->isEnabled()) { $this->LOGGER->info("Tpl fetching actually done for entity [{$ident}]. {$logMsg}"); FoldedResourcesManager::onEntityAction(FoldedResourcesManager::ACTION_ENTITY_FETCH_DONE, $this, $ident, $logMsg); } } catch (Exception $e) { /* * Произошла ошибка! * * Если мы устанавливали контенст и он не поменялся после завершения фетчинга (если поменялся, это ошибка), то нужно его обязательно завершить. * Если контекст был установлен во внешнем блоке, то этот блок должен позаботиться о сбросе контекста. * * Далее от нас требуется только пробросить ошибку наверх. */ if ($settedNow && $entity->equalTo(FoldedContextWatcher::getInstance()->getFoldedEntity())) { $CTXT->dropContext(); } throw $e; } $CONTENT = $PARAMS_FULL[FoldedTplFetchPrams::PARAM_CONTENT]; $PARAMS = $PARAMS_FULL; unset($PARAMS[FoldedTplFetchPrams::PARAM_CONTENT]); if ($PARAMS_KEY) { $this->saveToFoldedCache($PARAMS, $ident, $PARAMS_KEY); } if ($CONTENT_KEY) { $this->saveToFoldedCache($CONTENT, $ident, $CONTENT_KEY); } if ($settedNow) { $CTXT->dropContext(); } if ($addResources) { $CONTENT = $this->getResourcesLinks($ident, $CONTENT); $PARAMS_FULL[FoldedTplFetchPrams::PARAM_CONTENT] = $CONTENT; } switch ($returnType) { case self::FETCH_RETURN_FULL: return $RETURN_KEY ? $this->FETCH_RETURNS[$ident][$RETURN_KEY] = $PARAMS_FULL : $PARAMS_FULL; case self::FETCH_RETURN_FULL_OB: return $RETURN_KEY ? $this->FETCH_RETURNS[$ident][$RETURN_KEY] = new $PCLASS($PARAMS_FULL) : new $PCLASS($PARAMS_FULL); case self::FETCH_RETURN_CONTENT: return $RETURN_KEY ? $this->FETCH_RETURNS[$ident][$RETURN_KEY] = $CONTENT : $CONTENT; case self::FETCH_RETURN_PARAMS: return $RETURN_KEY ? $this->FETCH_RETURNS[$ident][$RETURN_KEY] = $PARAMS : $PARAMS; case self::FETCH_RETURN_PARAMS_OB: return $RETURN_KEY ? $this->FETCH_RETURNS[$ident][$RETURN_KEY] = new $PCLASS($PARAMS) : new $PCLASS($PARAMS); } raise_error("Unknown fetch return type [{$returnType}]."); }
/** * Метод, выполняющий всё вычисление и всю обработку */ private function doProcess($cells) { PsProfiler::inst(__CLASS__)->start('Unioning cells'); if ($this->LOGGER->isEnabled()) { $this->LOGGER->infoBox('Unioning ' . count($cells) . ' cells, user check: ' . var_export($this->checkUser, true)); $this->LOGGER->info(); $this->LOGGER->info('Before sorting and indexing:'); $this->LOGGER->info(print_r($cells, true)); } //Расположим ячейки от левого верхнего угла до правого нижнего (как при письме) usort($cells, function ($c1, $c2) { if ($c1['y_cell'] == $c2['y_cell']) { return $c1['x_cell'] > $c2['x_cell'] ? 1 : -1; } return $c1['y_cell'] > $c2['y_cell'] ? 1 : -1; }); $this->cells = array(); //Проиндексируем массив foreach ($cells as $cell) { $cell['x_cell'] = 1 * $cell['x_cell']; $cell['y_cell'] = 1 * $cell['y_cell']; $cell['id_user'] = 1 * $cell['id_user']; $this->cells[$cell['x_cell'] . 'x' . $cell['y_cell']] = $cell; } if ($this->LOGGER->isEnabled()) { $this->LOGGER->info(); $this->LOGGER->info('After sorting and indexing:'); $this->LOGGER->info(print_r($this->cells, true)); $this->LOGGER->info(); } //Пробегаем по всем ячейкам, объединяя их и исключая те ячейки, с которыми мы объединились $unioned = 0; foreach ($this->cells as $ident => $cell) { if (!array_key_exists($ident, $this->cells)) { //Видимо уже удаили ячейку из списка ячеек continue; } $x = $cell['x_cell']; $y = $cell['y_cell']; $id_user = $cell['id_user']; $dx = $this->getRightCellsCnt($x, $y, $id_user); if ($dx > 0) { //Объединяемся с ячейками справа $x1 = ($x - 1) * $this->w; $y1 = ($y - 1) * $this->h; $x2 = ($x + $dx) * $this->w; $y2 = $y * $this->h; for ($delta = 1; $delta <= $dx; $delta++) { $cident = $x + $delta . 'x' . $y; if ($this->LOGGER->isEnabled()) { $cid_user = array_get_value_in(array($cident, 'id_user'), $this->cells); $this->LOGGER->info(++$unioned . ". [{$ident}]({$id_user}) + [{$cident}]({$cid_user})"); } unset($this->cells[$cident]); } $this->result[] = array($x1, $y1, $x2, $y2, $id_user); continue; } $dy = $this->getBottomCellsCnt($x, $y, $id_user); if ($dy > 0) { //Объединяемся с ячейками снизу $x1 = ($x - 1) * $this->w; $y1 = ($y - 1) * $this->h; $x2 = $x * $this->w; $y2 = ($y + $dy) * $this->h; for ($delta = 1; $delta <= $dy; $delta++) { $cident = $x . 'x' . ($y + $delta); if ($this->LOGGER->isEnabled()) { $cid_user = array_get_value_in(array($cident, 'id_user'), $this->cells); $this->LOGGER->info(++$unioned . ". [{$ident}]({$id_user}) + [{$cident}]({$cid_user})"); } unset($this->cells[$cident]); } $this->result[] = array($x1, $y1, $x2, $y2, $id_user); continue; } //Не с кем объединяться, берём только эту ячейку $x1 = ($x - 1) * $this->w; $y1 = ($y - 1) * $this->h; $x2 = $x * $this->w; $y2 = $y * $this->h; $this->result[] = array($x1, $y1, $x2, $y2, $id_user); } $secundomer = PsProfiler::inst(__CLASS__)->stop(); if ($this->LOGGER->isEnabled()) { $this->LOGGER->info(); $this->LOGGER->info('Unioned cells:'); $this->LOGGER->info(print_r($this->result, true)); $this->LOGGER->info('Compression: {}%', round(count($this->result) / count($cells) * 100)); if ($secundomer) { $this->LOGGER->info('Done in ' . $secundomer->getAverage() . ' seconds'); } } }
protected function doAudit($action, $userId = null, $data = null, $saveToSession = false, $parentAction = null, $auditIfNoParent = true, $clearParent = true) { try { $action = $this->validateAction($action); $parentAction = $this->validateAction($parentAction, true); $actionSessionKey = $this->sessionCode($action); $parentActionSessionKey = $parentAction ? $this->sessionCode($parentAction) : null; $parentId = $parentActionSessionKey ? SessionArrayHelper::getInt($parentActionSessionKey) : null; $hasParentIdInSession = is_integer($parentId); $userId = AuthManager::validateUserIdOrNull($userId); $userIdAuthed = AuthManager::getUserIdOrNull(); if ($this->LOGGER->isEnabled()) { $this->LOGGER->info(); $this->LOGGER->info("<Запись #{}>", ++$this->NUM); $this->LOGGER->info('Действие: {}', $this->decodeAction($action)); $this->LOGGER->info('Пользователь: {}', is_inumeric($userId) ? $userId : 'НЕТ'); $this->LOGGER->info('Авторизованный пользователь: {}', is_inumeric($userIdAuthed) ? $userIdAuthed : 'НЕТ'); $this->LOGGER->info('Данные: {}', $data === null ? 'НЕТ' : print_r($data, true)); $this->LOGGER->info('Сохранять в сессию: {}', $saveToSession ? 'ДА' : 'НЕТ'); $this->LOGGER->info('Родительское действие: {}', $this->decodeAction($parentAction)); if ($parentAction) { $this->LOGGER->info('Родительское действие есть в сессии: {}', $hasParentIdInSession ? "ДА ({$parentActionSessionKey}={$parentId})" : 'НЕТ'); if ($hasParentIdInSession) { $this->LOGGER->info('Очищать родительское действие в сессии: {}', $clearParent ? 'ДА' : 'НЕТ'); } else { $this->LOGGER->info('Производить аудит при отсутствии родит. действия: {}', $auditIfNoParent ? 'ДА' : 'НЕТ'); } } } if (!$hasParentIdInSession && !$auditIfNoParent) { $this->LOGGER->info('АУДИТ НЕ ПРОИЗВЕДЁН!'); return; //--- Нужен предок, но его нет } $encoded = 0; if (is_array($data)) { if (count($data) == 0) { $data = null; } else { $data = self::encodeData($data); $encoded = 1; } } check_condition($data === null || is_string($data) || is_numeric($data), 'Illegal audit data type: ' . gettype($data) . ' for ' . $this); $recId = UtilsBean::inst()->saveAudit($parentId, $userId, $userIdAuthed, $this->PROCESS_CODE, $action, $data, $encoded); if ($this->LOGGER->isEnabled()) { if ($data !== null) { $this->LOGGER->info('Данные кодированы: {}', $encoded ? "ДА ({$data})" : 'НЕТ'); } $this->LOGGER->info('Информация сохранена в базу, id={}', $recId); } if ($saveToSession) { SessionArrayHelper::setInt($actionSessionKey, $recId); $this->LOGGER->info("Данные о действии сохранены в сессию ({$actionSessionKey}={$recId})"); } if ($hasParentIdInSession && $clearParent) { SessionArrayHelper::reset($parentActionSessionKey); $this->LOGGER->info('Данные о родительском действии удалены из сессии'); } $this->LOGGER->info('АУДИТ ПРОИЗВЕДЁН.'); } catch (Exception $ex) { //Не удалось записть аудит, но работа должна быть продолжена! ExceptionHandler::dumpError($ex); } }