Даже в коде Yii2 в yii/db/Query, где обработка indexBy идет индексация перебором rows в цикле, хотя есть способ использовать array_column для индексации массива по ключу. пример:
$users = [
['id' => 1, 'name' => 'Bob', 'age' => 30],
['id' => 12, 'name' => 'Alice', 'age' => 20],
['id' => 34, 'name' => 'Trump', 'age' => 69]
];
$users = array_column($users, null, 'id');
// В результате
$users = [
1 => ['id' => 1, 'name' => 'Bob', 'age' => 30],
12 => ['id' => 12, 'name' => 'Alice', 'age' => 20],
34 => ['id' => 34, 'name' => 'Trump', 'age' => 69]
];
Так же точно в качестве аналога ArrayHelper::map из Yii2 можно использовать этот же array_column
$users = [
['id' => 1, 'name' => 'Bob', 'age' => 30],
['id' => 12, 'name' => 'Alice', 'age' => 20],
['id' => 34, 'name' => 'Trump', 'age' => 69]
];
$users = array_column($users, 'id', 'name');
// В результате
$users = [
1 => 'Bob',
12 => 'Alice',
34 => 'Trump'
];
$str = '0123456789 abcdKDLAD !@#$%^&*()_+ абвгЖЗИКМ chỉđơngiảnlà 是印刷及排版';
$result = preg_replace('#(*UTF8)[^\pL\pN\s]+#u','', $str);
echo $result; // 0123456789 abcdKDLAD абвгЖЗИКМ chỉđơngiảnlà 是印刷及排版
Так-же можно удалять и пробелы убрав метасимвол \s.
Пусковой скрипт для запуска cron-команд в docker-контенере. Проверяет cron-выражение и запускает команду. Умеет выход по сигналам SIGTERM
и SIGINT
. В отличии от стандартного cron-демона следующую команду не запустит пока не выполнит предыдущую. Удобно использовать при разработке c docker-compose. В логах докера показывает что запускалось, когда, и с каким результатом.
#!/usr/bin/env php
<?php
/**
* Скрипт для запуска команды по расписанию, для замены cron-а в сети из docker-контейнеров.
*
* Пример команды, которая будет запускаться каждую минуту:
* docker/php/cron.php "* * * * *" php yii rate/update
*
* Зависимости:
* "mtdowling/cron-expression": "~1.2.0"
*
* @author Roman Zhuravlev <zhuravljov@gmail.com>
*/
require(__DIR__ . '/../../vendor/autoload.php');
// Params
$params = $_SERVER['argv'];
array_shift($params);
$expression = array_shift($params);
$command = implode(' ', $params);
$schedule = \Cron\CronExpression::factory($expression);
// Signal handler
$signal = 0;
$signalHandler = function ($sigNum) use (&$signal) {
$signal = $sigNum;
};
pcntl_signal(SIGTERM, $signalHandler);
pcntl_signal(SIGINT, $signalHandler);
// Loop
printLog('Cron started');
while (true) {
sleep(60 - time() % 60);
pcntl_signal_dispatch();
if ($signal) {
printLog("Cron stopped by signal $signal");
exit(0);
}
if (!$schedule->isDue()) {
continue;
}
$startedAt = microtime(true);
printLog("[started] command: $command", $startedAt);
passthru($command, $exitCode);
$finishedAt = microtime(true);
$totalTime = sprintf('%01.3f', $finishedAt - $startedAt);
printLog("[finished] duration $totalTime s, exit code $exitCode");
if ($exitCode) {
exit($exitCode);
}
}
function printLog($message, $time = null)
{
echo date('Y-m-d H:i:s', $time ?? time()), ' ' , $message, PHP_EOL;
}
Функция, позволяющая перевести PhpOffice Spreadsheet в таблицу PhpWord Table, Чтобы напечатать таблицу в .doc файле. Пришлось сделать эту функцию, потому что перевод XLS->HTML и HTML->DOC работает плохо для таблиц.
Из недочетов метода:
Делал этот метод для того, чтобы основная логика генерации таблиц была в XLS и чтобы генерация DOC использовала эту таблицу уже готовую и вставляла таблицу в заготовленный шаблон DOC.
private function convertXlsToDocTable(Worksheet $sheet, Table $table)
{
foreach ($sheet->getRowIterator() as $rowIndex => $row) {
$table->addRow();
$prevMergeRange = null;
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(false);
foreach ($cellIterator as $cellIndex => $cell) {
$value = $cell->getValue();
$mergeRange = $cell->getMergeRange();
if ($mergeRange && $prevMergeRange == $mergeRange) {
continue;
}
if ($mergeRange) {
$mergeCount = 0;
foreach ($row as $mergeCell) {
if ($mergeRange == $mergeCell['mergeRange']) {
$mergeCount++;
}
}
$table->addCell(2000, ['gridSpan' => $mergeCount])->addText($value);
$prevMergeRange = $mergeRange;
} else {
$table->addCell(2000, [])->addText($value);
}
}
}
return $table;
}
function urange($start, $end, $step)
{
if (!is_callable($step)) {
return range($start, $end, $step);
}
$helper = function ($current, $start) use ($end, $step, &$helper) {
if ($start > $end) {
return $current;
}
$current[] = $start;
return $helper($current, $step($start));
};
return $helper([], $start);
}
print_r(urange(20, 39, function ($n) {
$next = ($n / 10) % 10;
return $n + ($next >= 1 ? $next : 1);
}));
print_r(urange(1, 40, function ($n) {
return $n+1;
}));
print_r(urange(1, 40, function ($n) {
return $n+$n;
}));
При сохранении груповых данных, зачастую требуется сопоставить, то-что у нас уже хранится в БД и то-что пришло с формы, для этого приходится сверять элименты по уникальному полю, данный снипет, из одной незамысловатой строки, вернет массив с уникальными ключами. Затем можно уже проверять на isset($models_by_id[$id]) или array_diff_key, ну и т.д.
$models_by_id=Model::find()->indexBy('id')->all(); //yii 2 path @mista twista
$models=Model::model()->findAll(); //yii 1
$models_by_id=array_combine(array_keys(CHtml::listData($models, 'id', 'id')),$models);
$models_by_id=array_combine(array_column($model,'id'),$model); //since php5.5
<?php
namespace common\tests;
use Codeception\Util\Debug;
use yii\console\Application;
use yii\db\ActiveQuery;
use yii\db\Connection;
use yii\helpers\ArrayHelper;
use yii\helpers\Console;
use yii\helpers\VarDumper;
/**
* Useful set for performance optimization in console scripts, and test researches
*
* Trait ProfilingInspectorTrait
*
* @package common\tests
*/
trait ProfilingInspectorTrait
{
/**
* @param \yii\db\Query|ActiveQuery $query
* @param \yii\db\Connection $db
*/
protected function showQuery(\yii\db\Query $query, Connection $db)
{
$sql = $query->prepare($db->queryBuilder)->createCommand($db)->rawSql;
$this->resolvedOutput($sql, __FUNCTION__);
}
/**
* @param \yii\db\Query|ActiveQuery $query
* @param \yii\db\Connection $db
*/
protected function explainQuery(\yii\db\Query $query, Connection $db)
{
$sql = $query->prepare($db->queryBuilder)->createCommand($db)->rawSql;
$sql = 'EXPLAIN: ' . implode(PHP_EOL, $db->createCommand('EXPLAIN ' . $sql)->queryColumn('QUERY PLAN'));
$this->resolvedOutput($sql, __FUNCTION__);
}
/**
* @param \yii\db\Query|ActiveQuery $query
* @param \yii\db\Connection $db
*/
protected function analyzeQuery(\yii\db\Query $query, Connection $db)
{
$sql = $query->prepare($db->queryBuilder)->createCommand($db)->rawSql;
$sql = 'ANALYZE: ' . implode(PHP_EOL, $db->createCommand('EXPLAIN ANALYZE ' . $sql)->queryColumn('QUERY PLAN'));
$this->resolvedOutput($sql, __FUNCTION__);
}
/**
* Show execution time for specified callback and time elapsed from app start
* Return real function result
*
* @example
* $result=$this->timeIt(function(){
* $data = User::find()->where(['<','id', 100])->all();
* return ArrayHelper::map($data,'id','name');
* });
*
* @param \Closure $function
* @param string $comment Additional log comment
*
* @return mixed
*/
protected function timeIt(\Closure $function, string $comment = '')
{
$mStart = microtime(true);
$result = $function();
$mEnd = microtime(true);
$this->resolvedOutput(
[
'timeDelta' => ($mEnd - $mStart),
'elapsedTime'=>\Yii::getLogger()->elapsedTime
],
__FUNCTION__ . ':' . $comment
);
return $result;
}
/**
* Show execution time, memory usage, and peek memory check for specified callback
* Return real function result
*
* @example
* $result1=$this->inspectIt(function(){
* $data = User::find()->where(['<','id', 100])->all();
* return ArrayHelper::map($data,'id','name');
* },'Object result');
* $result2 = $this->inspectIt(function(){
* $data = User::find()->where(['<','id', 100])->asArray()->all();
* $result = ArrayHelper::map($data,'id','name');
* unset($data);
* return $result;
* },'As Array result');
*
* @param \Closure $function
* @param string $comment Additional log comment
*
* @return mixed
*/
protected function inspectIt(\Closure $function, string $comment = '')
{
$start = \Yii::getLogger()->elapsedTime;
$start_memory = memory_get_usage();
$start_peek = memory_get_peak_usage();
$result = $function();
$end_memory = memory_get_usage();
$end_peek = memory_get_peak_usage();
$end = \Yii::getLogger()->elapsedTime;
$this->resolvedOutput(
[
'timeDelta' => ($end - $start),
'memory' => [
'from' => \Yii::$app->formatter->asSize($start_memory),
'to' => \Yii::$app->formatter->asSize($end_memory),
'delta' => \Yii::$app->formatter->asSize(($end_memory - $start_memory)),
'peekMemoryIncreased' => ($end_peek > $start_peek) ? \Yii::$app->formatter->asSize($start_peek)
. ' +' . \Yii::$app->formatter->asSize($end_peek - $start_peek) : 'no',
],
],
__FUNCTION__ . ':' . $comment
);
return $result;
}
/**
* Show profile log for specified callback
* Return real function result
*
* @example
* $result=$this->profileIt(function(){
* $data = User::find()->where(['<','id', 100])->all();
* return ArrayHelper::map($data,'id','name');
* });
*
* @param \Closure $function
* @param string $comment Additional log comment
*
* @return mixed
*/
protected function profileIt(\Closure $function, string $comment = '')
{
$id = uniqid('profile_');
\Yii::beginProfile($id, __FUNCTION__ . ':' . $id);
$result = $function();
\Yii::endProfile($id, __FUNCTION__ . ':' . $id);
$profile = \Yii::getLogger()->getProfiling([]);
$map = ArrayHelper::getColumn($profile, 'info');
$profile = array_slice($profile, array_search($id, $map));
unset($map);
$this->resolvedOutput($profile, __FUNCTION__ . ':' . $id . ':' . $comment);
return $result;
}
/**
* @param $message
*/
protected function resolvedOutput($message, $subj = ''): void
{
$divider = '============= ' . $subj . ' ===============';
if (YII_ENV_TEST === true) {
Debug::debug($divider);
Debug::debug($message);
} elseif (\Yii::$app instanceof Application) {
Console::output(Console::ansiFormat($divider . PHP_EOL, [Console::FG_BLUE]));
Console::output(Console::ansiFormat(VarDumper::export($message) . PHP_EOL, [Console::FG_GREEN]));
} else {
\Yii::trace($message, get_called_class().':'.$subj);
}
}
}
API которое описанное в этом сниппете будет следовать следующим правилам:
v1.api.example.com
)Для написания API было использовано php + Symfony 3 + следующие бандлы:
Пакет создан для стандартизации всех сервисов по разгадыванию капч. У каждого сервиса есть свои особенности и теперь Вам надо будет всего лишь взглянуть на документацию конкретного сервиса чтобы правильно всё сделать. Так же пакет покрывает всю функциональсть сервисов. Если же Вам будет чего-то нехватать или будут предложения, я буду только рад их услышать.
<?php
function rgb2hex($rgb)
{
$hex = "#";
foreach ($rgb as $color) {
$hex .= str_pad(dechex($color), 2, "0", STR_PAD_LEFT);
}
return $hex;
}
$rgb = [175, 175, 175];
$hex = rgb2hex($rgb); // #afafaf