Пример #1
0
 /**
  * Основной метод получения экземпляра.
  * 
  * @param type $silentOnDoubleTry - признак, стоит ли нам ругаться, если мы 
  * обнаруживаем зацикливание при попытке получения экземпляра класса.
  * 
  * Это нужно для классов, которые выполняют сложную логику в конструкторе, которая
  * может привести к повторному вызову ::inst() внутри этого конструктора.
  * 
  * Классы, которые используют эту возможность:
  * @link DbChangeListener - менеджер прослушивания изменений в БД
  */
 protected static function inst($silentOnDoubleTry = false)
 {
     $class = get_called_class();
     if (array_key_exists($class, self::$_insts_)) {
         return self::$_insts_[$class];
     }
     if (array_key_exists($class, self::$_instsrq_)) {
         if ($silentOnDoubleTry) {
             return null;
         }
         raise_error("Double try to get singleton of [{$class}]");
     }
     self::$_instsrq_[$class] = true;
     //Создаём экземпляр
     $sec = Secundomer::startedInst("Creating singleton of {$class}");
     self::$_insts_[$class] = new $class();
     $sec->stop();
     //Экземпляр успешно создан
     unset(self::$_instsrq_[$class]);
     //Теперь добавим в профайлер. Всё это нужно для защиты от зацикливания.
     PsProfiler::inst(__CLASS__)->add($class, $sec);
     //Добавим к глобальному секундомеру - текущий
     $SECUNDOMER = self::$_secundomer_ ? self::$_secundomer_ : (self::$_secundomer_ = Secundomer::inst());
     $SECUNDOMER->addSecundomer($sec);
     //Отлогируем
     PsLogger::inst(__CLASS__)->info("+ {$class} ({$sec->getAverage()} / {$SECUNDOMER->getTotalTimeRounded()})");
     return self::$_insts_[$class];
 }
Пример #2
0
 /**
  * Метод регистрирует новый секундомер для идентификатора
  * 
  * @return Secundomer зарегистрированный идентификатор
  */
 private final function registerSecundomer($ident)
 {
     if (!array_key_exists($ident, $this->secundomers)) {
         $this->secundomers[$ident] = Secundomer::inst($this->profilerId . '::' . $ident);
     }
     return $this->secundomers[$ident];
 }
Пример #3
0
 public static final function finalize(PsLoggerInterface $LOGGER, $CONTENT)
 {
     //Проверим на пустоту
     if (isEmpty($CONTENT)) {
         return $CONTENT;
     }
     $call = ++self::$call;
     //Создадим экземпляр финализатора и вызовем его
     $class = get_called_class();
     $PROFILER = PsProfiler::inst($class);
     $inst = new $class($LOGGER, $PROFILER);
     $PROFILER->start(__FUNCTION__);
     $SECUNDOMER = Secundomer::startedInst();
     $LOGGER->infoBox(">>> CALLED {$call}. {$class}");
     try {
         $CONTENT = $inst->doFinalize($CONTENT);
         $PROFILER->stop();
         $SECUNDOMER->stop();
     } catch (Exception $ex) {
         $PROFILER->stop(false);
         $LOGGER->infoBox("Exception occured while calling {$class}::finalize. Message: " . $ex->getMessage());
         throw $ex;
     }
     $LOGGER->infoBox("<<< CALL {$call}. {$class} FINISHED IN " . $SECUNDOMER->getAverage() . ' seconds');
     return $CONTENT;
 }
Пример #4
0
function doTest()
{
    global $HOST;
    global $TOTAL;
    global $HREFS;
    global $REQUESTS_CNT;
    $RESULTS = array();
    $j = 0;
    foreach ($HREFS as $href => $name) {
        ++$j;
        $href = ensure_starts_with($href, '/');
        $path = "http://{$HOST}{$href}";
        $sec = Secundomer::inst();
        for ($index = 0; $index <= $REQUESTS_CNT; $index++) {
            if ($index == 0) {
                //Пропустим первый вызов, на случай кеширования
                file_get_contents($path);
                continue;
            }
            $sec->start();
            file_get_contents($path);
            $sec->stop();
            ++$TOTAL;
        }
        dolog(pad_zero_left($j, 2) . '/' . count($HREFS) . " [{$path}] - " . $sec->getAverage() . ' (' . $sec->getTotalTime() . '/' . $sec->getCount() . ')');
        $RESULTS[$path] = str_replace('.', ',', round($sec->getAverage(), 2));
    }
    asort($RESULTS, SORT_DESC);
    $RESULTS = array_reverse($RESULTS, true);
    return $RESULTS;
}
Пример #5
0
/**
 * Процесс строит скрипты для вставки ячеек в БД
 * 
 * @param array $argv
 */
function executeProcess(array $argv)
{
    $DM = DirManager::inst(array(__DIR__, 'output'));
    $customNum = $argv[1];
    dolog('Processing mosaic demo, custom num={}', $customNum);
    /* @var $dir DirItem */
    foreach ($DM->getDirContent(null, DirItemFilter::DIRS) as $dir) {
        if (is_inumeric($customNum) && $customNum != $dir->getName()) {
            continue;
            //----
        }
        $imgDM = DirManager::inst($dir->getAbsPath());
        $imgDI = end($imgDM->getDirContent(null, DirItemFilter::IMAGES));
        $map = $imgDM->getDirItem(null, 'map', 'txt')->getFileAsProps();
        $demoDM = DirManager::inst($imgDM->absDirPath(), 'demo');
        $demoDM->clearDir();
        dolog("Building map for: [{}].", $imgDI->getRelPath());
        //CELLS BINDING
        $dim = $imgDM->getDirItem(null, 'settings', 'txt')->getFileAsProps();
        $dim = $dim['dim'];
        $dim = explode('x', $dim);
        $cw = 1 * $dim[0];
        $ch = 1 * $dim[1];
        $sourceImg = SimpleImage::inst()->load($imgDI);
        $w = $sourceImg->getWidth();
        $h = $sourceImg->getHeight();
        $destImg = SimpleImage::inst()->create($w, $h, MosaicImage::BG_COLOR);
        dolog("Img size: [{$w} x {$h}].");
        check_condition($w > 0 && !($w % $cw), 'Bad width');
        check_condition($h > 0 && !($h % $ch), 'Bad height');
        $totalCells = count($map);
        $lengtn = strlen("{$totalCells}");
        //dolog("Cells cnt: [$xcells x $ycells], total: $totalCells.");
        //СТРОИМ КАРТИНКИ
        $secundomer = Secundomer::startedInst();
        //$encoder = new PsGifEncoder();
        for ($cellCnt = 0; $cellCnt <= $totalCells; $cellCnt++) {
            $name = pad_zero_left($cellCnt, $lengtn);
            $copyTo = $demoDM->absFilePath(null, $name, 'jpg');
            if ($cellCnt > 0) {
                $cellParams = $map[$cellCnt];
                $cellParams = explode('x', $cellParams);
                $xCell = $cellParams[0];
                $yCell = $cellParams[1];
                $x = ($xCell - 1) * $cw;
                $y = ($yCell - 1) * $ch;
                $destImg->copyFromAnother($sourceImg, $x, $y, $x, $y, $cw, $ch);
            }
            $destImg->save($copyTo);
            dolog("[{$totalCells}] {$copyTo}");
        }
        //$encoder->saveToFile($demoDM->absFilePath(null, 'animation'));
        $secundomer->stop();
        dolog('Execution time: ' . $secundomer->getTotalTime());
    }
}
Пример #6
0
 /**
  * Вызов выполнения метода. Используется из ajax.
  */
 public static function execute($method, array $params)
 {
     check_condition(array_key_exists($method, self::getMethodsList()), "Method TestManager::{$method} cannot be called");
     PsUtil::startUnlimitedMode();
     PsLogger::inst('TestManager')->info("Method TestManager::{$method} called with params: " . array_to_string($params));
     $s = Secundomer::startedInst();
     call_user_func_array(array(TestManager::inst(), $method), $params);
     $s->stop();
     PsLogger::inst('TestManager')->info("Call done in {$s->getTotalTime()} seconds");
 }
Пример #7
0
 public static function testProductivity($callback, $count = 1000)
 {
     PsDefines::assertProductionOff(__CLASS__);
     check_condition(is_callable($callback), 'Передан некорректный callback для тестирования');
     $s = Secundomer::inst();
     for ($index = 0; $index < $count; $index++) {
         $s->start();
         call_user_func($callback);
         $s->stop();
     }
     return $s;
 }
Пример #8
0
 /**
  * Перестраиваем и сохраняем classpath в файл.
  * Вынесли это действие в отдельный метод, чтобы можно было вывести classpath в файл (для дебага).
  * 
  * @return AutoloadDir
  */
 public function rebuild()
 {
     if (!$this->rebuilded) {
         $this->rebuilded = true;
         $s = Secundomer::startedInst();
         $this->pathes = array();
         PsCoreIncluder::loadClassPath($this->classesDir->getAbsPath(), $this->pathes, false);
         $this->classPathFile->saveArrayToFile($this->pathes);
         $this->classPathFile->getSibling($this->classPathFile->getName() . '_arr')->putToFile(print_r($this->pathes, true));
         $this->LOGGER->info("{$this} rebuilded in {$s->stopAndGetAverage()} seconds.");
     }
     return $this;
 }
Пример #9
0
 public function buildContent()
 {
     PsUtil::startUnlimitedMode();
     $s = Secundomer::inst();
     $data = array();
     for ($num = 0; $num <= self::REQUESTS_COUNT; $num++) {
         $s->start();
         file_get_contents($this->url);
         $s->stop();
         $data[$num]['time'] = $s->getTime();
         $data[$num]['total'] = $s->getTotalTime();
         if ($num == 0) {
             //Запросим один раз, чтобы сработало кеширование, если оно включено
             $s->clear();
         }
     }
     $params = array('url' => $this->url, 'data' => $data, 'average' => $s->getAverage());
     echo $this->getFoldedEntity()->fetchTpl($params);
 }
Пример #10
0
 public function closeAndWriteFinalLog()
 {
     $this->secundomer->stop();
     $this->doWrite("Shell command execution finished in {$this->secundomer->getAverage()} seconds.");
 }
Пример #11
0
 /**
  * Парсит файл профайлинга и возвращает массив вида:
  * ident=>Secundomer
  * Идентификатор здесь, это идентификатор профилируемой сущности, например - текст запроса.
  */
 private function parseProfiler(DirItem $file)
 {
     $result = array();
     $lines = $file->getFileLines(false);
     if (empty($lines)) {
         return $result;
         //---
     }
     foreach ($lines as $line) {
         $tokens = explode('|', $line);
         if (count($tokens) != 3) {
             continue;
         }
         $ident = trim($tokens[0]);
         $count = trim($tokens[1]);
         $time = trim($tokens[2]);
         if (!$ident || !is_numeric($count) || !is_numeric($time)) {
             continue;
         }
         if (!array_key_exists($ident, $result)) {
             $result[$ident] = Secundomer::inst();
         }
         $result[$ident]->add($count, $time);
     }
     return $result;
 }
Пример #12
0
 /**
  * Метод выполняет дамп таблицы
  */
 public static function dumpTable($idColumn, $table, array $where = array(), $order = null)
 {
     //Стартуем секундомер
     $secundomer = Secundomer::startedInst("Снятие дампа {$table}");
     //Отключим ограничение по времени
     PsUtil::startUnlimitedMode();
     //Получим экземпляр логгера
     $LOGGER = PsLogger::inst(__CLASS__);
     //Текущий пользователь
     $userId = AuthManager::getUserIdOrNull();
     //Для логов, запросов и все остального
     $table = strtoupper(PsCheck::tableName($table));
     //Макс кол-во записей
     $limit = PsDefines::getTableDumpPortion();
     //Проверим наличие id
     $idColumn = PsCheck::tableColName($idColumn);
     $LOGGER->info('Dumping table {}. Id column: {}, limit: {}. User id: {}.', $table, $idColumn, $limit, $userId);
     //Получаем лок (без ожидания)
     $lockName = "DUMP table {$table}";
     $locked = PsLock::lock($lockName, false);
     $LOGGER->info('Lock name: {}, locked ? {}.', $lockName, var_export($locked, true));
     if (!$locked) {
         return false;
         //Не удалось получить лок
     }
     $zipDi = false;
     try {
         //ЗПРОСЫ:
         //1. Запрос на извлечение колва записей, подлежащих дампированию
         $queryCnt = Query::select("count({$idColumn}) as cnt", $table, $where);
         //2. Запрос на извлечение данных, подлежащих дампированию
         $queryDump = Query::select('*', $table, $where, null, $order, $limit);
         //3. Запрос на извлечение кодов дампируемых записей
         $selectIds = Query::select($idColumn, $table, $where, null, $order, $limit);
         //4. Запрос на удаление дампированных данных
         $queryDel = Query::delete($table, Query::plainParam("{$idColumn} in (select {$idColumn} from (" . $selectIds->build($delParams) . ') t )', $delParams));
         //Выполним запрос для получения кол-ва записей, подлежащих дампу
         $cnt = PsCheck::int(array_get_value('cnt', PSDB::getRec($queryCnt, null, true)));
         $LOGGER->info('Dump recs count allowed: {}.', $cnt);
         if ($cnt < $limit) {
             $LOGGER->info('SKIP dumping table, count allowed ({}) < limit ({})...', $cnt, $limit);
             $LOGGER->info("Query for extract dump records count: {$queryCnt}");
             PsLock::unlock($lockName);
             return false;
         }
         //Время дампа
         $date = PsUtil::fileUniqueTime(false);
         $time = time();
         //Название файлов
         $zipName = $date . ' ' . $table;
         //Элемент, указывающий на zip архив
         $zipDi = DirManager::stuff(null, array(self::DUMPS_TABLE, $table))->getDirItem(null, $zipName, PsConst::EXT_ZIP);
         $LOGGER->info('Dump to: [{}].', $zipDi->getAbsPath());
         if ($zipDi->isFile()) {
             $LOGGER->info('Dump file exists, skip dumping table...');
             PsLock::unlock($lockName);
             return false;
         }
         //Комментарий к таблице
         $commentToken[] = "Date: {$date}";
         $commentToken[] = "Time: {$time}";
         $commentToken[] = "Table: {$table}";
         $commentToken[] = "Manager: {$userId}";
         $commentToken[] = "Recs dumped: {$limit}";
         $commentToken[] = "Total allowed: {$cnt}";
         $commentToken[] = "Query dump cnt:   {$queryCnt}";
         $commentToken[] = "Query dump data:  {$queryDump}";
         $commentToken[] = "Query dump ids:   {$selectIds}";
         $commentToken[] = "Query del dumped: {$queryDel}";
         $comment = implode("\n", $commentToken);
         //Начинаем zip и сохраняем в него данные
         $zip = $zipDi->startZip();
         $zip->addFromString($zipName, serialize(PSDB::getArray($queryDump)));
         $zip->setArchiveComment($comment);
         $zip->close();
         $LOGGER->info('Data successfully dumped, zip comment:');
         $LOGGER->info("[\n{$comment}\n]");
         //Удалим те записи, дамп которых был снят
         $LOGGER->info('Clearing dumped table records...');
         $affected = PSDB::update($queryDel);
         $LOGGER->info('Rows deleted: {}.', $affected);
         $LOGGER->info('Dumping is SUCCESSFULLY finished. Total time: {} sec.', $secundomer->stop()->getAverage());
     } catch (Exception $ex) {
         PsLock::unlock($lockName);
         ExceptionHandler::dumpError($ex);
         $LOGGER->info('Error occured: {}', $ex->getMessage());
         throw $ex;
     }
     return $zipDi;
 }
Пример #13
0
    PsSecurity::set(new PsSecurityProviderCmd());
} else {
    /*
     * Автоматически подключаемся к БД
     */
    PsConnectionPool::configure(PsConnectionParams::production());
}
/*
 * Инициализируем окружение, если мы работаем под ним.
 * Подключаемое окружение может установить свой провайдер безопасности.
 * Важно! Вызов не перемещать в if, так как метод init должен быть вызван обязательно.
 */
PsEnvironment::init();
/*
 * Инициализируем подсистему безопасности
 */
PsSecurity::init();
//Зарегистрируем функцию, подключающую админские ресурсы
function ps_admin_on($force = false)
{
    if ($force || AuthManager::isAuthorizedAsAdmin()) {
        Autoload::inst()->registerAdminBaseDir();
    }
}
//Ну и сразу попытаемся подключить админские ресурсы
ps_admin_on();
//Подключаем файл глобальных настроек, если он существует и мы работаем в рамках проекта
PsGlobals::init();
//Получим экземпляр профайлера, чтобы подписаться на PsShotdown, если профилирование включено
PsProfiler::inst()->add('ScriptInit', Secundomer::inst()->add(1, microtime(true) - SCRIPT_EXECUTION_START));
Пример #14
0
     $ch = 1 * $dim[1];
 }
 //Скопируем свойства, с которыми она создавалась
 $outDm->getDirItem(null, 'settings', 'txt')->writeToFile('dim=' . $cw . 'x' . $ch);
 dolog("Cell dimensions: [{$cw} x {$ch}].");
 $w = $img->getImageAdapter()->getWidth();
 $h = $img->getImageAdapter()->getHeight();
 dolog("Img size: [{$w} x {$h}].");
 check_condition($w > 0 && !($w % $cw), 'Bad width');
 check_condition($h > 0 && !($h % $ch), 'Bad height');
 $xcells = $w / $cw;
 $ycells = $h / $ch;
 $totalCells = $xcells * $ycells;
 dolog("Cells cnt: [{$xcells} x {$ycells}], total: {$totalCells}.");
 $generator = new MosaicCellsGenerator($totalCells);
 $secundomer = Secundomer::startedInst();
 $sqlDI = $outDm->getDirItem(null, 'fill', 'sql');
 $mapDI = $outDm->getDirItem(null, 'map', 'txt');
 $sqlImg = "delete from ps_img_mosaic_parts where id_img={$id};";
 $sqlDI->writeLineToFile($sqlImg);
 $sqlImg = "delete from ps_img_mosaic_answers where id_img={$id};";
 $sqlDI->writeLineToFile($sqlImg);
 $sqlImg = "delete from ps_img_mosaic where id_img={$id};";
 $sqlDI->writeLineToFile($sqlImg);
 $sqlImg = "insert into ps_img_mosaic (id_img, w, h, cx, cy, cw, ch) values ({$id}, {$w}, {$h}, {$xcells}, {$ycells}, {$cw}, {$ch});";
 $sqlDI->writeLineToFile($sqlImg);
 for ($cellCnt = 1; $cellCnt <= $totalCells; $cellCnt++) {
     $cellNum = $generator->getCellNum();
     $xCell = $cellNum % $xcells;
     $xCell = $xCell == 0 ? $xcells : $xCell;
     $yCell = ($cellNum - $xCell) / $xcells + 1;
Пример #15
0
/**
 * Процесс строит скрипты для вставки ячеек в БД
 * 
 * @param array $argv
 */
function executeProcess(array $argv)
{
    $DM = DirManager::inst(__DIR__);
    //Перенесём картинки в директорию со всеми картинками
    /* @var $img DirItem */
    foreach ($DM->getDirContent(null, DirItemFilter::IMAGES) as $img) {
        $img->copyTo($DM->absFilePath('source', $img->getName()));
        $img->remove();
    }
    //Очистим директорию с выходными файлами
    $DM->clearDir('output');
    $DM->makePath('output');
    //Пробегаемся по картинкам, разбиваем их на ячейки и строим sql для вставки
    /* @var $img DirItem */
    foreach ($DM->getDirContent('source', DirItemFilter::IMAGES) as $img) {
        $id = $img->getNameNoExt();
        //Обрабатываем только картинки с целыми кодами
        if (is_inumeric($id)) {
            dolog("Processing img [{$img->getRelPath()}].");
            $id = 1 * $id;
        } else {
            dolog("Skip img [{$img->getRelPath()}], name is not numeric.");
            continue;
            //---
        }
        //Начало обработки
        $outDm = DirManager::inst(array(__DIR__, 'output'), $id);
        //Скопируем картинку
        $img->copyTo($outDm->absFilePath(null, $img->getName()));
        //Вычислим размеры ячейки
        $cw = 10;
        $ch = 10;
        $dim = $DM->getDirItem('source', $id, 'txt')->getFileAsProps(false);
        $dim = $dim['dim'];
        if ($dim) {
            $dim = explode('x', $dim);
            $cw = 1 * $dim[0];
            $ch = 1 * $dim[1];
        }
        //Скопируем свойства, с которыми она создавалась
        $outDm->getDirItem(null, 'settings', 'txt')->putToFile('dim=' . $cw . 'x' . $ch);
        dolog("Cell dimensions: [{$cw} x {$ch}].");
        //Размеры картинки получим у самой картинки
        $w = $img->getImageAdapter()->getWidth();
        $h = $img->getImageAdapter()->getHeight();
        dolog("Img size: [{$w} x {$h}].");
        check_condition($w > 0 && !($w % $cw), 'Bad width');
        check_condition($h > 0 && !($h % $ch), 'Bad height');
        $xcells = $w / $cw;
        $ycells = $h / $ch;
        $totalCells = $xcells * $ycells;
        dolog("Cells cnt: [{$xcells} x {$ycells}], total: {$totalCells}.");
        $generator = new MosaicCellsGenerator($totalCells);
        $secundomer = Secundomer::startedInst();
        $sqlDI = $outDm->getDirItem(null, 'fill', 'sql');
        $mapDI = $outDm->getDirItem(null, 'map', 'txt');
        $sqlImg = "delete from ps_img_mosaic_parts where id_img={$id};";
        $sqlDI->writeLineToFile($sqlImg);
        $sqlImg = "delete from ps_img_mosaic_answers where id_img={$id};";
        $sqlDI->writeLineToFile($sqlImg);
        $sqlImg = "delete from ps_img_mosaic where id_img={$id};";
        $sqlDI->writeLineToFile($sqlImg);
        $sqlImg = "insert into ps_img_mosaic (id_img, w, h, cx, cy, cw, ch) values ({$id}, {$w}, {$h}, {$xcells}, {$ycells}, {$cw}, {$ch});";
        $sqlDI->writeLineToFile($sqlImg);
        for ($cellCnt = 1; $cellCnt <= $totalCells; $cellCnt++) {
            $cellNum = $generator->getCellNum();
            $xCell = $cellNum % $xcells;
            $xCell = $xCell == 0 ? $xcells : $xCell;
            $yCell = ($cellNum - $xCell) / $xcells + 1;
            $sqlCell = "insert into ps_img_mosaic_parts (id_img, n_part, x_cell, y_cell) values ({$id}, {$cellCnt}, {$xCell}, {$yCell});";
            $sqlDI->writeLineToFile($sqlCell);
            $mapDI->writeLineToFile($cellCnt . '=' . $xCell . 'x' . $yCell);
        }
        $secundomer->stop();
        dolog('Execution time: ' . $secundomer->getTotalTime());
    }
}
Пример #16
0
 private function doSolve($rebus)
 {
     $this->LOGGER->info("Обрабатываем ребус: [{$rebus}], пропускать первые символы={$this->SCIP_FIRST_CHARS}");
     $this->COMBINATIONS = array();
     check_condition($rebus, "Пустой ребус");
     $eqCnt = substr_count($rebus, '=');
     check_condition($eqCnt == 1, "Знак равенства '=' встретился {$eqCnt} раз");
     $this->LOGGER->info("Приведённый вид: [{$rebus}]");
     $enCh = array();
     $ruCh = array();
     for ($i = 0; $i < ps_strlen($rebus); $i++) {
         //русский символ
         $letter = ps_charat($rebus, $i);
         if ($this->isRuChar($letter)) {
             if (!array_key_exists($letter, $ruCh)) {
                 $ruCh[$letter] = null;
             }
             continue;
         }
         if ($this->isEnChar($letter)) {
             //английский символ
             if (!in_array($letter, $enCh)) {
                 $enCh[] = $letter;
             }
             continue;
         }
     }
     $this->LOGGER->info('Русские символы: ' . print_r($ruCh, true));
     $this->LOGGER->info('Английские символы: ' . print_r($enCh, true));
     foreach ($ruCh as $ch => $value) {
         for ($i = 0; $i < strlen($this->EN); $i++) {
             $letter = substr($this->EN, $i, 1);
             if (!in_array($letter, $enCh)) {
                 $enCh[] = $letter;
                 $ruCh[$ch] = $letter;
                 break;
             }
         }
     }
     $this->LOGGER->info('После привязки: ');
     $this->LOGGER->info('Русские символы: ' . print_r($ruCh, true));
     $this->LOGGER->info('Английские символы: ' . print_r($enCh, true));
     $enCharsCnt = count($enCh);
     check_condition($enCharsCnt > 0, 'Нет символов для перебора');
     check_condition($enCharsCnt <= 10, "Слишком много переменных: {$enCharsCnt}");
     $rebus = PsStrings::replaceMap($rebus, $ruCh);
     $this->LOGGER->info("Подготовленный ребус: [{$rebus}]");
     $this->LOGGER->info("Всего символов для перебора: {$enCharsCnt}");
     $this->LOGGER->info('Возможных комбинаций: ' . $this->variantsCnt($enCharsCnt));
     $this->REBUS = $rebus;
     $this->EXPR = str_replace('=', '-', $rebus);
     $hasBefore = false;
     for ($i = 0; $i < strlen($rebus); $i++) {
         $char = substr($rebus, $i, 1);
         if ($this->isEnChar($char)) {
             //Символ перебора
             if (!$hasBefore) {
                 $this->FIRST_CHARS[] = $char;
                 $hasBefore = true;
             }
         } else {
             $hasBefore = false;
         }
     }
     $this->LOGGER->info('Начинаем перебор...');
     $numbers = array(0 => false, 1 => false, 2 => false, 3 => false, 4 => false, 5 => false, 6 => false, 7 => false, 8 => false, 9 => false);
     $secundomer = Secundomer::startedInst();
     for ($i = 0; $i <= 9; $i++) {
         $letter = reset($enCh);
         if ($this->isScip($i, $letter)) {
             continue;
         }
         $next = next($enCh);
         $numbers[$i] = $letter;
         $this->doCheckIteration($enCh, $next, $numbers);
         $numbers[$i] = false;
     }
     $secundomer->stop();
     $parsed = DatesTools::inst()->parseSeconds(round($secundomer->getTotalTime()));
     $min = $parsed['mf'];
     $sec = pad_zero_left($parsed['s'], 2);
     $parsed = "{$min}:{$sec}";
     $combCnt = count($this->COMBINATIONS);
     $this->LOGGER->info("Перебор закончен. Обработано операций: {$this->cnt}, найдено решений: {$combCnt}.");
     $this->LOGGER->info("Общее время обработки: {$parsed}.");
     return $this->COMBINATIONS;
 }