常用php函数总结

<?php
/**
 * 常用且复杂的方法,虽然基于YII框架写的,但是并不是和Yii紧耦合,如果在其他框架使用,简单修改即可,我已经在laravel 和 CI 框架均使用过此代码库
 */

namespace app\library;

use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Exception;
use Yii;
use yii\helpers\Json;
use yii\httpclient\Client;
use yii\log\FileTarget;
use yii\helpers\FileHelper;

class PublicFunction
{

    /**
     * 日志
     *
     * @param string $path
     * @return void
     */
    static public function logs($path = 'PubFunction')
    {

        $argv = func_get_args();
        array_shift($argv);

        $txt = '';
        foreach ($argv as $val) {
            if (is_array($val) || is_object($val)) {
                $txt .= print_r($val, 1) . "\n";
            } else {
                $txt .= $val . "\n";
            }
        }

        $time = microtime(true);
        $log = new FileTarget();
        $log->maxFileSize = 51200;
        $log->maxLogFiles = 50;
        $log->logFile = Yii::$app->getRuntimePath() . '/logs/' . $path . '/' . date('Ymd') . '.log';
        $log->messages[] = ["\n" . $txt . "\n------------------------------------------------------------------------", 4, 'pubfunction', $time];
        $logPath = dirname($log->logFile);
        if ((!is_dir($logPath)) && (!file_exists($logPath))) {
            FileHelper::createDirectory($logPath, 0777, true);
        }

        $log->export();
    }

    /**
     * 获取当前登录者
     *
     * @return array
     */
    static public function getCurrentUser()
    {
        if (property_exists(Yii::$app->controller, 'user')) {
            return Yii::$app->controller->user;
        } else {
            return [];
        }

    }

    /**
     * 从电子表格中读数据放到第二个参数中
     * @param string $file 文件路径
     * @param array $return 读出的数据
     * @return number 返回sheet的偏移量
     */
    static public function readDataInSpreadsheet($file, &$return)
    {
        $spreadsheet = IOFactory::load($file);
        $return = $spreadsheet->getActiveSheet()->toArray(null, true, true, true);
        return $spreadsheet->getIndex($spreadsheet->getActiveSheet());
    }

    /**
     * 从一个大的excel中读取数据
     *
     * @param string $file 文件位置
     * @param callable $callable 处理一行的回掉函数
     * @param string $maxColumn 处理最大的列号,超过此列的列号将忽略
     * @return void
     * @throws CommandImportIgnoreException 收到此异常,将退出循环,不在继续检索下一个行
     */
    static public function readDataInBigSpreadsheet($file, callable $callable, $maxColumn = 'AC')
    {
        echo '开始执行前' . round(memory_get_usage() / 1024 / 1024, 3) . PHP_EOL;
        $spreadsheet = IOFactory::load($file);
        $sheet = $spreadsheet->getActiveSheet();
        echo '加在excel之后' . round(memory_get_usage() / 1024 / 1024, 3) . PHP_EOL;
//        exit;
        $maxCol = $sheet->getHighestColumn();

        $maxCol = Coordinate::columnIndexFromString($maxCol) > Coordinate::columnIndexFromString($maxColumn) ? $maxColumn : $maxCol;

        $maxRow = $sheet->getHighestRow();
        $err = null;
        foreach ($sheet->getRowIterator() as $row) {
            $lineIndex = $row->getRowIndex();
            $values = [];
            foreach ($row->getCellIterator() as $cell) {
                $column = $cell->getColumn();
                $values[$column] = static::trim($cell->getValue());
//                if($lineIndex == 128)var_dump($values[$column]);
                if (Coordinate::columnIndexFromString($column) >= Coordinate::columnIndexFromString($maxCol)) break;
            }
            try {
                call_user_func_array($callable, [$values, $lineIndex]);
            } catch (CommandImportIgnoreException $e) {//忽略异常,则退出循环
                break;
            } catch (\Exception $e) {
                $err = $e;
                break;
            }

            if ($lineIndex >= $maxRow) break;
        }
        echo '处理之后' . round(memory_get_usage() / 1024 / 1024, 3) . PHP_EOL;
        $spreadsheet->disconnectWorksheets();
        unset($spreadsheet);
        $sheet->disconnectCells();
        unset($sheet);
        unset($maxCol);

        gc_collect_cycles();
        echo '回收内存之后' . round(memory_get_usage() / 1024 / 1024, 3) . PHP_EOL;
        if ($err) {
            throw $err;
        }
    }

    /**
     * 导出 .xlsx
     * @param string $title 文件名
     * @param array $header 行头,eg:['姓名','性别', '年龄']
     * @param array $data 数据,eg:
     * [
     *      [
     *          '王大',
     *          '男',
     *          15,
     *      ],
     *      [
     *          '王大',
     *          '男',
     *           15,
     *      ],
     * ]
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     * @throws Exception
     */
    static public function export($title, $header, &$data)
    {
        $spreadsheet = new Spreadsheet();

        $spreadsheet->getProperties()
            ->setTitle($title);
        $handle = $spreadsheet->setActiveSheetIndex(0);
        foreach ($header as $index => $item) {
            $handle->setCellValue(Coordinate::stringFromColumnIndex($index + 1) . "1", $item);
        }

        $loop = 2;

        static::exportBody($title, $loop, $data, $handle, $spreadsheet);
    }

    static public function exportBody($title, $loop, $data, Worksheet $handle, Spreadsheet $spreadsheet)
    {
        if (is_callable($data)) {
            $data = call_user_func($data);
        }

        foreach ($data as $values) {
            $values = array_values($values);
            foreach ($values as $index => $val) {
                $val = is_array($val) ? implode(',', $val) : $val;
                $handle->setCellValue(Coordinate::stringFromColumnIndex($index + 1) . $loop, $val);
            }
            $loop++;
        }

        $spreadsheet->setActiveSheetIndex(0);

        static::exportEnd($title, $spreadsheet);
    }

    static public function exportEnd($title, Spreadsheet $spreadsheet)
    {
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment;filename=' . $title . '.xlsx');
        header('Cache-Control: max-age=0');

        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
        header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
        header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
        header('Pragma: public'); // HTTP/1.0

        $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
        $writer->save('php://output');
        exit;
    }

    /**
     * 输出大文件,使用 Spout 重构,Spout号称:Spout needs only 3MB of memory to process any file.
     * https://opensource.box.com/spout/
     *
     * @param string $fileName 文件名
     * @param array $headers 表头
     * @param \Closure $callable 闭包,返回数据迭代器
     * @param boolean $toBrowser 是否输出到浏览器
     * @param \Closure $sheets 闭包,返回sheets迭代器,导出文件包含其他sheet时,使用此参数简单扩充
     * [
     *  'sheetName' => 'sheet 名字,非必要',
     *  'header' => [],//表头,array
     *  'callback' => callable(), //\Generator 数据迭代器
     * ]
     *
     * eg:
     * PublicFunction::exportBigData('111', [1, 2, 3], call_user_func( function () {
            yield [1, 2, 3];
            yield ['a', 'b', 'c'];
            yield [1, 2, 3];
        }), false, call_user_func( function () {
            yield [
                'sheetName' => 'other sheet 1',
                'header' => ['s11','s12','s13'],//表头,array
                'data' => call_user_func(function () {
                    yield [1, 2, 3];
                    yield ['a', 'b', 'c'];
                    yield [1, 2, 3];
                })
            ];
            yield [
                'sheetName' => 'other sheet 2',
                'header' => ['s21','s22','s23'],//表头,array
                'data' => call_user_func(function () {
                    yield [1, 2, 3,6];
                    yield ['a', 'b', 'c'];
                    yield [1, 2, 3];
                })
            ];
        }));
     * @throws \Box\Spout\Common\Exception\IOException
     * @throws \Box\Spout\Writer\Exception\WriterNotOpenedException
     */
    static public function exportBigData($fileName, $headers, \Iterator $data, $toBrowser = true, \Iterator $sheets = null)
    {
//        $spreadsheet = new Spreadsheet();
//
//        $spreadsheet->getProperties()
//            ->setTitle($fileName);
//        $handle = $spreadsheet->setActiveSheetIndex(0);
//        foreach ($headers as $index => $item) {
//            $handle->setCellValue(Coordinate::stringFromColumnIndex($index + 1) . "1", $item);
//        }
//
//        $data = call_user_func($callable);
//        $loop = 2;
//        foreach ($data as $values) {
//            foreach ($values as $index => $val) {
//                $val = is_array($val) ? implode(',', $val) : $val;
//                $handle->setCellValue(Coordinate::stringFromColumnIndex($index + 1) . $loop, $val);
//            }
//            $loop++;
//        }
//
//        $spreadsheet->setActiveSheetIndex(0);
//
//        $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
//        if($toBrowser){
//            header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
//            header('Content-Disposition: attachment;filename=' . $fileName . '.xlsx');
//            header('Cache-Control: max-age=0');
//
//            header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
//            header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
//            header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
//            header('Pragma: public'); // HTTP/1.0
//
//            $writer->save('php://output');
//        }else{
//            $writer->save($fileName);
//        }
//
//        exit;

        $writer = WriterEntityFactory::createXLSXWriter();
        $writer->setShouldUseInlineStrings(false);

        $fileName = substr($fileName, -5) == '.xlsx' ? $fileName : ($fileName . '.xlsx');
        if ($toBrowser) {
            $writer->openToBrowser($fileName);
        } else {
            $writer->openToFile($fileName);
        }

        $writerFunc = function ($headers, $data, $writer) {
            $h = WriterEntityFactory::createRowFromArray($headers);
            $writer->addRow($h);
            foreach ($data as $row) {
                if ($row) {
                    foreach ($row as $k => $v) {
                        if (is_array($v)) {
                            $row[$k] = implode(',', $v);
                        } else {
                            $row[$k] = strval($v);
                        }
                    }

                    $r = WriterEntityFactory::createRowFromArray($row);
                    $writer->addRow($r);
                }
            }
        };

        $writerFunc($headers, $data, $writer);

        if ($sheets) {
            foreach ($sheets as $sheetData) {
                $sheet = $writer->addNewSheetAndMakeItCurrent();
                if (!empty($sheetData['sheetName'])) {
                    $sheet->setName($sheetData['sheetName']);
                }
                $writerFunc($sheetData['header'], $sheetData['data'], $writer);
            }
        }
        $writer->close();

        if ($toBrowser) {
            exit;
        }
    }

    static public function exportZip($fileName, $headers, \yii\db\Query $query, $callable, $exportAfterCallback)
    {
        $subProcess = new SubProcess(8);
        $limit = 5000;
        $page = 0;
        $tmpDir = sys_get_temp_dir() . '/export/' . \uniqid();
        $sourceDir = $tmpDir . '/' . $fileName . '/';
        $toFilename = $tmpDir . '/' . $fileName . '.zip';
        if (!is_dir($sourceDir)) {
            FileHelper::createDirectory($sourceDir);
        }

        $total = $query->count();
        if ($total == 0) {
            throw new MCNException("导出结果为空");
        }
        $pageTotal = ceil($total / $limit);

        while (1) {
            if ($page == $pageTotal) break;
            $offset = $page * $limit;
            if ($page == $pageTotal - 1) {
                $limit = $total % $limit;
            }
            $subProcess->do(function () use ($sourceDir, $offset, $limit, $callable, $headers, $query) {
                Yii::$app->db->close();
                $fileName = $sourceDir . ($offset + 1) . '-' . ($offset + $limit) . '.xlsx';
                static::exportBigData($fileName, $headers, function () use ($callable, $query, $offset, $limit) {
                    foreach ($callable($query, $offset, $limit) as $item) {
                        yield $item;
                    }
                }, false);
            });

            $page++;
        }
        $subProcess->wait();
//        while (1){
//            var_dump($sourceDir, $toFileDir.$fileName.'.zip');
//            sleep(10);
//        }
        HZip::zipDir($sourceDir, $toFilename);

        $exportAfterCallback($toFilename);
        FileHelper::removeDirectory($tmpDir);
    }

    /**
     * 输出大文件
     * @param string $fileName 文件名
     * @param array $headers 表头
     * @param callable $callable 返回一条数据,代表一行
     * @throws \Box\Spout\Common\Exception\IOException
     * @throws \Box\Spout\Writer\Exception\WriterNotOpenedException
     */
    static public function exportBigData2($fileName, $headers, $callable)
    {
        $writer = WriterEntityFactory::createXLSXWriter();
//        $writer->openToFile($filePath);
        $writer->openToBrowser($fileName . '.xlsx');
        $writer->setShouldUseInlineStrings(false);

        $h = WriterEntityFactory::createRowFromArray($headers);
        $writer->addRow($h);
        $data = call_user_func($callable);
        foreach ($data as $row) {
            if ($row) {
                foreach ($row as $k => $v) {
                    if (is_array($v)) {
                        $row[$k] = implode(',', $v);
                    } else {
                        $row[$k] = strval($v);
                    }
                }

                $r = WriterEntityFactory::createRowFromArray($row);
                $writer->addRow($r);
            }
        }
        $writer->close();
        \Yii::$app->end(\Yii::$app::STATE_END);
    }

    /**
     * 判断文件夹是否存在,如果不存在则创建
     *
     * @param [type] $dir
     * @return void
     * @throws \yii\base\Exception
     */
    static public function checkDir($dir)
    {
        if (!is_dir($dir)) {
            FileHelper::createDirectory($dir, 0777, true);
        }
    }

    /**
     * 格式化字符串
     *
     * @param [type] $txt
     * @param integer $lenght
     * @return void
     */
    static public function formatString($txt, $lenght = 10)
    {
        if (mb_strlen($txt) > $lenght) {
            return mb_substr($txt, 0, $lenght - 3) . '...';
        } else {
            return $txt;
        }
    }

    /**
     * 下载csv,支持utf8
     *
     * @param [type] $filename
     * @return void
     */
    static public function importCSV($filename)
    {
        $result = [];
        if (($handle = fopen($filename, 'r')) !== FALSE) {
            $i = 0;
            while (($data = fgetcsv($handle, 0, ",")) !== FALSE) {
                $isempty = true;
                array_walk($data, function (&$a) use (&$isempty) {
                    $a = trim($a);
                    if ($a != '') {
                        $t = mb_detect_encoding($a, ["GB18030", "UTF-8", "GB2312", "GBK", "BIG5"]);
                        if ($t !== false && $t != "UTF-8") {
                            $a = mb_convert_encoding($a, 'UTF-8', $t);
                        }
                        $isempty = false;
                    }
                });

                if ($isempty) continue;

                $result[] = $data;
            }
            fclose($handle);
        }

        return $result;
    }

    /**
     * 下载excel
     *
     * @param array $data
     * @param string $file_name
     * @return void
     */
    static public function downloadCsv($data = [], $file_name = '')
    {
        ob_clean();
        header('Content-Type: application/vnd.ms-excel');
        header('Content-Disposition: attachment;filename=' . $file_name);
        header('Cache-Control: max-age=0');
        $fp = fopen('php://output', 'a');
        fwrite($fp, "\xEF\xBB\xBF");
        foreach ($data as $v) {
            echo $v['data'];
        }
        fclose($fp);
    }


    /**
     * 下载cfv,支持unicode
     *
     * @param array $data
     * @param string $file_name
     * @return void
     */
    static public function downloadCsv2($data = [], $file_name = '')
    {
        ob_clean();
        header('Content-Type: application/vnd.ms-excel');
        header('Content-Disposition: attachment;filename=' . $file_name);
        header('Cache-Control: max-age=0');
        $fp = fopen('php://output', 'a');
        fwrite($fp, "\xEF\xBB\xBF");
        foreach ($data as $v) {
            echo implode(',', $v) . "\n";
        }
        fclose($fp);
    }

    /**
     * 写入csv文件首行
     *
     * @param string $path 保存路径
     * @param string $fileName 文件名
     * @param array $header 写入的首行
     * @param integer $time 等待延时
     * @param string $size 内存大小
     * @return void 返回文件指针
     */
    static public function downloadCsvHeader($path, $fileName, $header = array(), $time = 0, $size = '512M')
    {
        set_time_limit($time);
        ini_set('memory_limit', $size);
        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }
        $fp = fopen($path . $fileName, 'w');
        fputcsv($fp, $header);
        return $fp;
    }

    /**
     * 支持中文的rtrim
     *
     * @param string $string 待处理字符串
     * @param string $trim_chars
     * @return void
     */
    static function mb_rtrim($string, $trim_chars = '\s')
    {
        return preg_replace('/(.*?)[' . $trim_chars . ']*$/u', '\\1', $string);
    }

    /**
     * 是否时手机号
     *
     * @param number $phone 待检查手机号
     * @return boolean
     */
    public static function isPhone($phone)
    {
//        return preg_match('/^(13|14|15|16|17|18|19)[0-9]{9}$/', $phone);
        return preg_match('/^1[0-9]{10}$/', $phone);
    }

    /**
     * 检查是不是日期类型字符串
     *
     * @param string $date 待检查字符串
     * @return bool
     */
    public static function checkDateFormat($date)
    {
        $format = [
            'Y-m-d',
            'Y/m/d',
            'Y-n-j',
            'Y/n/j'
        ];

        foreach ($format as $item) {
            if ($date == date($item, strtotime($date))) {
                return true;
            }
        }

        return false;
    }

    /**
     * object/数组转换为json字符串
     * @param array|object $data
     * @return string
     */
    static function array_jsonencode($data)
    {
        return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }

    /**
     * 获取反转数据,支持数组返回
     * @param $data
     * @param null $value
     * @param bool $key
     * @param bool $showName
     * @return array
     */
    static function getFlip($data, $value = null, $key = true, $showName = true)
    {
        foreach ($data as $k => &$v) {
            $v['id'] = $k;
        }

        $result = array_column($data, ($showName ? 'name' : ($key ? 'alias' : 'id')), ($key ? 'id' : 'alias'));

        return $value !== null ? (isset($result[$value]) ? $result[$value] : '参数错误') : $result;
    }

    /**
     * 得到客户端IP
     * @param int $type 返回id类型,0:返回ip,1:返回long值
     * @return string
     */
    static function getClientIps($type = 0)
    {
        $type = $type ? 1 : 0;
        static $ip = NULL;
        if ($ip !== NULL) return $ip[$type];
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $pos = array_search('unknown', $arr);
            if (false !== $pos) unset($arr[$pos]);
            $ip = trim($arr[0]);
        } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif (isset($_SERVER['REMOTE_ADDR'])) {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        // IP地址合法验证
        $long = sprintf("%u", ip2long($ip));
        $ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
        return $ip[$type];
    }

    /**
     * 字符串去重
     * @param $str
     * @return string
     */
    static function unique($str)
    {
        $arr = explode(',', $str);
        $arr = array_unique($arr);
        $data = implode(',', $arr);
        $data = trim($data, ',');
        return $data;
    }

    
    /**
     * 获取网络资源,等价 curl 等工具,
     * @param string $sUrl 资源链接
     * @param string $method post|get 等
     * @param array $data 资源参数
     * @param bool $jsonFormat 在post请求中,请求体是否为json格式
     * @param int $errTimes 当前错误次数
     * @param int $maxTimes 最大重试次数
     * @param string $logCategory 日志主题
     * @param array $headers 添加额外的header
     * @return string|null 返回响应字符串
     * @throws \yii\base\InvalidConfigException
     */
    public static function getResBody($sUrl, $method, $data = [], $jsonFormat = false, $errTimes = 0, $maxTimes = 0, $logCategory = 'getResBody', $headers = [])
    {
        if ($errTimes > $maxTimes) {
            \Yii::info("已经尝试{$errTimes}次,均链接异常,目标地址失联", $logCategory);
            throw new \Exception("已经尝试{$errTimes}次,均链接异常,目标地址失联");
        }

        try {
            $timer = PublicFunction::getTimer();
            $client = new Client([
                'transport' => 'yii\httpclient\CurlTransport' // only cURL supports the options we need
            ]);

            $connectTimeout = 5;
            $timeout = 15;
            $req = $client->createRequest()
                ->setMethod($method)
                ->setUrl($sUrl)
                ->setOptions([
                    CURLOPT_CONNECTTIMEOUT => $connectTimeout, // connection timeout
                    CURLOPT_TIMEOUT => $timeout, // data receiving timeout
                ]);
            if ($headers) {
                $req->setHeaders($headers);
            }

            if ($jsonFormat) {
                $req->setFormat(Client::FORMAT_JSON);
            }

            if ($data) {
                $req->setData($data);
            };
            $res = $req->send();
            \Yii::info($sUrl . PHP_EOL . '请求时间' . $timer() . PHP_EOL . var_export(compact('method', 'data', 'jsonFormat', 'errTimes', 'maxTimes', 'logCategory', 'headers', 'res'), 1), $logCategory);
            return $res->getContent();
        } catch (\yii\httpclient\Exception $e) {
            if ($e->getCode() == 28) {//接收数据超时
                \Yii::info($sUrl . PHP_EOL . ' 接口超时timeout' . ($errTimes + 1) . '次,接口超时时间' . $timeout, $logCategory);
                return static::getResBody($sUrl, $method, $data, $jsonFormat, $errTimes + 1, 20);
            }
            \Yii::info($sUrl . PHP_EOL . '链接请求超时connectTimeout' . ($errTimes + 1) . '次,超时时间' . $connectTimeout, $logCategory);
            usleep(500);
            return static::getResBody($sUrl, $method, $data, $jsonFormat, $errTimes + 1, 10);
        }
    }

    /**
     * 获取计时器,debug工具
     */
    public static function getTimer()
    {
        $begin = microtime(true);
        return function ($format = true) use ($begin) {
            $sec = bcsub(microtime(true), $begin);
            if (!$format) {
                return $sec;
            }

            $sec = round($sec);
            $m = floor($sec / 60);
            $h = 0;

            if ($m) {
                $sec = $sec % 60;
                $h = floor($m / 60);
                if ($h) {
                    $m = $m % 60;
                }
            }
            $res = "";
            if ($h) {
                $res .= $h . '小时';
                $res .= $m . '分';
                $res .= $sec . '秒';
            } else if ($m) {
                $res .= $m . '分';
                $res .= $sec . '秒';
            } else {
                $res .= $sec . '秒';
            }
            return $res;
        };
    }

    /**
     * trim
     */
    public static function trim($str)
    {
        $search = array(" ", " ", "\n", "\r", "\t", " ", "\r\t");
        $replace = "";
        return str_replace($search, $replace, $str);
    }


    /**
     * 使用项其他的前缀redis链接执行方法
     *
     * @param string $prefix
     * @param callable $callable
     * @return mixed
     */
    public static function doOtherRedis($prefix, $callable)
    {
        return call_user_func_array($callable, [static::getOtherRedis($prefix)]);
    }

    /**
     * 获取其他前缀redis链接
     * @param $prefix
     * @return Redis
     */
    public static function getOtherRedis($prefix): Redis
    {
        $prefix .= ':';
        static $conns = [], $config = [];
        if (key_exists($prefix, $conns)) {
            return $conns[$prefix];
        }
        if (!$config) {
            $config = require Yii::$app->basePath . '/config/redis.php';
        }
        $config['options']['prefix'] = $prefix;

        $conns[$prefix] = Yii::createObject($config);
        return $conns[$prefix];
    }


    /**
     * 获取
     * @param string $dateTime datetime
     * @return float
     */
    public static function diffToToday($dateTime)
    {
        return round((time() - strtotime($dateTime)) / 86400, 2);
    }

    /**
     * 删除数组中的某个值
     * @param $ary
     * @param $val
     * @return array  被移除后的数组
     */
    public static function arrayRemoveVal($ary, $val)
    {

        if (($index = array_search($val, $ary)) !== false) {
            array_splice($ary, $index, 1);
        }
        return $ary;
    }

    /**
     * 兼容utf8的parse_url
     * @param $url
     * @return mixed
     */
    public static function mb_parse_url($url)
    {
        $enc_url = preg_replace_callback(
            '%[^:/@?&=#]+%usD',
            function ($matches) {
                return urlencode($matches[0]);
            },
            $url
        );

        $parts = parse_url($enc_url);

        if ($parts === false) {
            throw new \InvalidArgumentException('Malformed URL: ' . $url);
        }

        foreach ($parts as $name => $value) {
            $parts[$name] = urldecode($value);
        }

        return $parts;
    }

    /**
     * 生成签名
     * @param $data
     * @param $apiSecret
     * @return string
     */
    static public function getSign($data, $apiSecret)
    {
        unset($data['smidgn5']);
        unset($data['data']);
        ksort($data);
        $stringA = '';
        foreach ($data as $key => $value) {
            $value = str_replace(' ', '+', $value);
            $stringA .= $key . '=' . $value . '&';
        }
        $stringA = trim($stringA, '&');
        $stringSignTemp = $stringA . '&key=' . $apiSecret;
        $signTmp = strtoupper(md5($stringSignTemp));
        return $signTmp;
    }


    public static function convert($size)
    {
        $unit = array('b', 'kb', 'mb', 'gb', 'tb', 'pb');
        return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $unit[$i];
    }

    /**
     * 代理下载网络资源
     * @param string $filename 资源链接
     * @param null|\Closure $downloadAfter 下载后执行回的调方法
     */
    public static function download($filename, $downloadAfter = null)
    {
        //设置脚本的最大执行时间,设置为0则无时间限制
        set_time_limit(0);
        ini_set('max_execution_time', '0');

        //通过header()发送头信息
        //因为不知道文件是什么类型的,告诉浏览器输出的是字节流
        header('content-type:application/octet-stream');

        //告诉浏览器返回的文件大小类型是字节
        header('Accept-Ranges:bytes');

        //获得文件大小
        $filesize = filesize($filename);//(此方法无法获取到远程文件大小)
//        $header_array = get_headers($filename, true);

//        $filesize = $header_array['Content-Length'];

        //告诉浏览器返回的文件大小
        header('Accept-Length:' . $filesize);
        //告诉浏览器文件作为附件处理并且设定最终下载完成的文件名称
        header('content-disposition:attachment;filename=' . basename($filename));


        //针对大文件,规定每次读取文件的字节数为4096字节,直接输出数据
        $read_buffer = 2048;
        $handle = fopen($filename, 'rb');

        @ob_clean();
        @ob_end_clean();
        $sum_buffer = 0;
        while (!feof($handle)) {
            echo fread($handle, $read_buffer);
            $sum_buffer += $read_buffer;
            @ob_flush();
            @flush();
        }

        //关闭句柄
        fclose($handle);
        if ($downloadAfter) {
            call_user_func_array($downloadAfter, []);
        }
        exit;
    }

    public static function randomStr($count = 6)
    {
        $base = 'abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ23456789';
        $ret = '';
        $strlen = strlen($base);
        for ($i = 0; $i < $count; ++$i) {
            $ret .= $base[random_int(0, $strlen - 1)];
        }

        return $ret;
    }

    public static function numeric2Num($val)
    {
        if (is_array($val)) {
            array_walk($val, function (&$v) {
                $v = static::numeric2Num($v);
            });
        } elseif (is_numeric($val)) {
            $val = $val - 0;
        }

        return $val;
    }

    public static function getAgeFromBirthDay($birthday)
    {
        $bday = new \DateTime($birthday); // 你的出生日

        $today = new \Datetime(date('Y-m-d'));

        $diff = $today->diff($bday);
        return $diff->y;
    }

    static $locks = [];

    public static function lock($name, $ex = 60)
    {
        $redis = Yii::$app->redis;
        $val = uniqid();
        if ($redis->set($name, $val, 'NX', 'EX', $ex)) {
            register_shutdown_function(function () use ($name, $val) {
                PublicFunction::unlock($name, $val);
            });
        } else {
            throw new \Exception('执行中,请稍等');
        }
        return $val;
    }

    public static function unlock($name, $val)
    {
        static $releaseLuaScript = <<<LUA
if redis.call("GET",KEYS[1])==ARGV[1] then
    return redis.call("DEL",KEYS[1])
else
    return 0
end
LUA;
        return Yii::$app->redis->eval($releaseLuaScript, 1, $name, $val);
    }

    /**
     * 按首字母大写把字符串分隔成数组
     * @param $str
     * @return array|false|string[]
     */
    public static function FUSplitStr($str)
    {
        return preg_split('/(?<=[a-z0-9])(?=[A-Z])/x', $str);
    }

    /**
     * 把关联数组的健由下划线转成驼峰
     * @param array $val
     * @return array
     */
    public static function camelizeField($val)
    {
        foreach ($val as $f => $v) {
            unset($val[$f]);
            $val[static::camelize($f)] = $v;
        }
        return $val;
    }

    /**
     *   * 下划线转驼峰
     *   * 思路:
     *   * step1.原字符串转小写,原字符串中的分隔符用空格替换,在字符串开头加上分隔符
     *   * step2.将字符串中每个单词的首字母转换为大写,再去空格,去字符串首部附加的分隔符.
     * @param $uncamelized_words
     * @param string $separator
     * @return string
     */
    public static function camelize($uncamelized_words, $separator = '_')
    {
        $uncamelized_words = $separator . str_replace($separator, " ", strtolower($uncamelized_words));
        return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator);
    }

    /**
     * 驼峰命名转下划线命名
     * @param $camelCaps
     * @param string $separator
     * @return string
     */
    public static function uncamelize($camelCaps, $separator = '_')
    {
        return strtolower(preg_replace('/(?<=[a-z])([A-Z])/', $separator . '$1', $camelCaps));
    }


    /**
     * 获取对象的反射句柄
     * @param $class
     * @return \ReflectionClass
     */
    public static function getReflection($class)
    {
        static $handles = [];
        if (!isset($handles[$class])) {
            $handles[$class] = new \ReflectionClass($class);
        }
        return $handles[$class];
    }

    public static function getConstantsNameByVal($class, $prefix, $val)
    {
        static $contents = [];
        if (!isset($contents[$class][$prefix])) {
            $contents[$class][$prefix] = array_flip(static::getConstantsInClass($class, $prefix));
        }
        if (isset($contents[$class][$prefix][$val])) return substr($contents[$class][$prefix][$val], strlen($prefix));
        throw new \Exception('常量不存在');
    }

    /**
     * 获取一个类中指定的常量
     * @param string $className 类的名字
     * @param string|null $prefix 常量前缀
     * @return array
     */
    public static function getConstantsInClass($className, $prefix = null)
    {
        $objClass = static::getReflection($className);
        $arrConst = $objClass->getConstants();
        if (!$prefix) {
            return $arrConst;
        }

        $res = [];
        foreach ($arrConst as $k => $v) {
            if (strpos($k, $prefix) === 0) {
                $res[$k] = $v;
            }
        }
        return $res;
    }

    /**
     * file_get_contents post 请求
     * @param $url
     * @param $curlPost
     * @return mixed
     */
    public static function http_post($url, $curlPost)
    {
        $query = http_build_query($curlPost);

        $options['http'] = array(
            'timeout' => 60,
            'method' => 'POST',
            'header' => 'Content-type:application/x-www-form-urlencoded',
            'content' => $query
        );

        $context = stream_context_create($options);
        $result = file_get_contents($url, false, $context);
        $userInfo = json_decode($result, true);

        return $userInfo;
    }

    /*
     * redis
     * 用scan实现keys
     * $limit 获取数量,0不限
     * $prefix是否需要连接配置前缀
     * */
    public static function redisScan($pattern, $limit = 0, $prefix = true)
    {
        $data = [];
        $cursor = 0;
        if ($prefix === true) $pattern = redis()->options['prefix'] . $pattern;//是否添加前缀
        do {
            list($cursor, $keys) = redis()->scan($cursor, 'MATCH', $pattern);
            $cursor = (int)$cursor;
            if (!empty($keys)) {
                $data = array_merge($data, $keys);
                if (count($keys) >= $limit and $limit) break;
            }
        } while ($cursor !== 0);
        return $limit ? array_slice($data, 0, $limit) : $data;
    }
}
此条目发表在php, php函数集, 基础, 程序基础分类目录,贴了, 标签。将固定链接加入收藏夹。