PHP简单写个日志文件的写入和读取功能

方文锋  2023-07-14 18:09:28  4642  首页学习PHP

PHP简单写个日志文件的写入和读取功能。

代码如下:

 <?php
/**
 * Created by PhpStorm.
 * User: admin
 * Date: 2023/7/11
 * Time: 11:16
 */

class Log
{
    private $conf = array(
        "expire" => 0,
        "suffix" => "log",
        "key_split" => "◀", //字段分隔标记
        "row_split" => "▟", //行尾分隔标记
        "exp" => 7200, //默认2小时后过期
        "replace" => array(),
        "add_data" => "",
    );
    //字段列表
    private $field_list = array("add_time");
    //日志文件目录
    private $dir_path = "";

    public function __construct()
    {
        date_default_timezone_set("Asia/Shanghai");
    }

    /**
     * @param array|string $key
     * @param string $val
     * @return $this
     */
    public function set($key = '', $val = '')
    {
        $list = array("field_list", "dir_path");
        if (self::is_string($key) && self::exist($val)) {
            in_array($key, $list, true) ? $this->{$key} = $val : $this->conf[$key] = $val;
        } else {
            if (is_array($key)) {
                foreach ($key as $k => $item) {
                    self::set($k, $item);
                }
            }
        }
        return $this;
    }

    public function get($key = '')
    {
        $list = array("field_list", "dir_path");
        if (self::is_string($key)) {
            return in_array($key, $list) ? $this->{$key} : $this->conf[$key];
        }
        return array("conf" => $this->conf, "field_list" => $this->field_list, "dir_path" => $this->dir_path);
    }

    public function init()
    {
        $this->conf['expire'] = time() + 3600;
        $this->conf['suffix'] = 'log';
        $this->conf['key_split'] = '◀';
        $this->conf['row_split'] = '▟';
        $this->conf['exp'] = 7200;
        $this->conf['replace'] = array("key_split" => "[[ks]]", "row_split" => "[[rs]]", "br" => "[[br]]");
        $this->conf['add_data'] = '';
        $this->field_list = array('add_time', 'ip', 'url', 'method', 'protocol', 'user_agent', 'accept');
        $this->dir_path = '';
        return $this;
    }

    /**
     * 设置日志目录路径
     * @param string $path 绝对路径|‘/’开头路径
     * @param string|null $root_dir 绝对路径
     * @return $this
     */
    public function set_dir_path($path = '', $root_dir = '')
    {
        self::is_string($path) && ($this->dir_path = $path);
        $dir = $this->dir_path;
        $is_relative = self::is_string($dir) && strpos($dir, '/') === 0;
        if (self::is_string($root_dir)) {
            $this->dir_path = $root_dir;
            if (empty($dir)) {
                $dir = $root_dir . '/log/';
            } elseif ($is_relative) {
                $dir = rtrim($root_dir, '\\/') . $dir;
            }
        } else {
            if (empty($dir)) {
                $dir = dirname(__FILE__) . "/../../log/";
            } elseif ($is_relative) {
                if ($_SERVER["DOCUMENT_ROOT"]) {
                    $dir = rtrim($_SERVER["DOCUMENT_ROOT"], '/\\') . $dir;
                } else {
                    $dir = dirname(__FILE__) . "/../../" . $dir;
                }
            }
        }
        $dir = preg_replace("/[\\\\\/]{1,}/i", "/", $dir);
        if (!is_dir($dir)) {
            mkdir($dir, 0777, true);
        }
        $this->dir_path = $dir;
        return $this;
    }

    /**
     * 设置字段列表
     * @param array $list 数字下标一维数组
     * @return $this
     */
    public function set_field_list($list = array())
    {
        if (is_array($list) && count($list) > 0) {
            if (self::is_assoc($list)) {
                $temp = array();
                foreach ($list as $k => $v) {
                    $temp[] = self::is_string($v) ? $v : $k;
                }
                $list = $temp;
            }
            $this->field_list = $list;
        }
        return $this;
    }

    /**
     * 过期时间
     * @param int $exp 多少秒后过期
     * @return $this
     */
    public function expire($exp = 0)
    {
        if (is_numeric($exp)) {
            $exp = intval($exp);
            ($exp <= 0) && ($exp = 31622400); // 1年
            $this->conf['exp'] = $exp;
            $this->conf['expire'] = time() + $exp;
        }
        return $this;
    }

    /**
     * 日志文件后缀
     * @param string $name
     * @return $this
     */
    public function suffix($name = 'log')
    {
        self::is_string($name) && ($this->conf['suffix'] = $name);
        return $this;
    }

    /**
     * 构成写入日志的数据
     * @param string|array $data
     * @return $this
     */
    public function data($data = '')
    {
        $split = array($this->conf['key_split'], $this->conf['row_split']);
        if (empty($data)) {
            $this->field_list = array('add_time', 'ip', 'url', 'method', 'protocol', 'user_agent', 'accept');
            $data = array(
                'ip' => self::ip(),
                'url' => self::get_url('url'),
                'add_time' => date('Y-m-d H:i:s'),
                'method' => $_SERVER['REQUEST_METHOD'],
                'protocol' => $_SERVER['SERVER_PROTOCOL'],
                'user_agent' => $_SERVER['HTTP_USER_AGENT'],
                'accept' => $_SERVER['HTTP_ACCEPT'],
            );
        }
        if (is_array($data) && self::arrayLevel($data) == 1) {
            $data = self::handle_data_string($this->field_list, [$data]);
        }
        $this->conf['add_data'] = $data;
        return $this;
    }

    /**
     * 设置日志文件头部信息
     * @param array $data
     * @param string $file_name
     * @return string
     */
    public function set_head_info($data = array(), $file_name = '')
    {
        $split = array($this->conf['key_split'], $this->conf['row_split']);
        $t = time();
        $conf = array(
            "create_time" => array("time" => $t, "datetime" => date("Y-m-d H:i:s")),
            "expire" => array("time" => $this->conf['expire'], "datetime" => date("Y-m-d H:i:s", $this->conf['expire']),),
            "field_list" => $this->field_list,
        );
        if (is_array($data) && count($data) > 0) {
            $conf = array_merge($conf, $data);
        }
        $temp = serialize($conf) . "{$split[0]}\n";
        $temp .= "[field_list]  " . join(',', $this->field_list) . "\n";
        $temp .= "{$split[1]}\n";
        return $temp;
    }

    /**
     * 获取日志文件头部记录信息
     * @param string $file_path
     * @return bool|mixed
     */
    public function get_head_data($file_path = '')
    {
        if (self::is_string($file_path)) {
            $arr = self::file_rows($file_path, 1, 1);
            $str = rtrim($arr[0], $this->conf['key_split']);
            return unserialize($str);
        }
        return false;
    }

    /**
     * 数组转换维字符串
     * @param array $key_list 一维数组
     * @param array $data 一维数组或者二维数组
     * @return array|bool|string
     */
    public function handle_data_string($key_list = array(), $data = array())
    {
        if (is_array($key_list) && count($key_list) > 0) {
            if (self::is_assoc($key_list)) {
                foreach ($key_list as $k => $v) {
                    $temp[] = self::is_string($v) ? $v : $k;
                }
                $key_list = $temp;
                unset($temp);
            }
            if (is_array($data) && count($data) > 0) {
                $split = array($this->conf['key_split'], $this->conf['row_split']);
                $lv = self::arrayLevel($data);
                if ($lv == 1) {
                    if (self::is_assoc($data)) {
                        foreach ($data as $key => $val) {
                            $sk = array_search($key, $key_list);
                            is_numeric($sk) && ($temp[$sk] = self::text_in($val));
                        }
                        ksort($temp);
                        $data = $temp;
                        $temp = join($split[0], $data);
                        return $temp;
                    }
                    return join($split[0], self::text_in($data));
                } elseif ($lv == 2) {
                    $temp = "";
                    foreach ($data as $index => $val) {
                        $temp .= self::handle_data_string($key_list, $val) . "{$split[1]}\n";
                    }
                    return $temp;
                }
            }
        }
        return false;
    }

    /**
     * 字符串转数组,返回一维或二维数组
     * @param array $key_list 一维数组
     * @param string $str
     * @param bool $assoc
     * @return array|bool
     */
    public function handle_string_data($key_list = array(), $str = '', $assoc = true)
    {
        if (is_array($key_list) && count($key_list) > 0) {
            $split = array($this->conf['key_split'], $this->conf['row_split']);
            if (self::is_assoc($key_list)) {
                foreach ($key_list as $k => $v) {
                    $temp[] = self::is_string($v) ? $v : $k;
                }
                $key_list = $temp;
                unset($temp);
            }
            if (preg_match("/{$split[1]}/i", $str)) {
                $data = explode($split[1], $str);
                if (!self::is_string(trim($data[count($data)-1]))) {
                    unset($data[count($data) - 1]);
                }
                $temp = array();
                foreach ($data as $index => $v) {
                    $temp[] = self::handle_string_data($key_list, $v, $assoc);
                }
                return $temp;
            } elseif (preg_match("/{$split[0]}/i", $str)) {
                $data = explode($split[0], $str);
                foreach ($data as $index => $v) {
                    $v = self::text_out(trim($v));
                    $data[$index] = $v;
                    $data[$key_list[$index]] = $v; //使用键值访问
                    if ($assoc) {
                        unset($data[$index]);
                    }
                }
                return $data;
            }
        }
        return false;
    }

    /**
     * 历史过期日志文件信息列表
     * @param string $file_name
     * @param array|string $data
     * @return array|bool
     */
    public function history_link($file_name = '', $data = '', $write = false)
    {
        $key_list = array("expire_file_name", "expire_file_path");
        $dir_path = rtrim($this->dir_path, '/');
        $name = "history_link_data_{$file_name}.{$this->conf['suffix']}";
        $file_path = "$dir_path/$name";
        if (file_exists($file_path) && $write === false) {
            //读取数据和更新数据
            $str = file_get_contents($file_path);
            $temp = self::handle_string_data($key_list, $str);
            return $temp;
        } else {
            //创建文件和写入数据
            if (is_array($data) && count($data) > 0) {
                $lv = self::arrayLevel($data);
                if ($lv < 3) {
                    ($lv === 1) && ($data = array($data));
                    $data = self::handle_data_string($key_list, $data);
                    $f = fopen($file_path, "a+");
                    fwrite($f, $data);
                    return fclose($f);
                }
            }
        }
        return false;
    }

    /**
     * 日志文件过期时的操作
     * @param string $file_name
     * @param int $expire
     * @return bool
     */
    public function file_expire($file_name = '', $expire = 3600)
    {
        $t = time();
        $dir_path = rtrim($this->dir_path, '/');
        $suffix = $this->conf['suffix'];
        $head = self::get_head_data($file_name);
        if ($t > $head['expire']['time']) {
            //到期code
            $c_dt = date("Y-m-d+H-i-s", $head['create_time']['time']);
            $file_path = "$dir_path/$file_name.{$suffix}";
            $re_name = "expire_{$head['create_time']['time']}_{$c_dt}_$file_name"; //重命名,保存创建时间信息
            $new_file_path = "$dir_path/$re_name.$suffix";
            if (file_exists($file_path)) {
                self::history_link($file_name, array("expire_file_name" => "$re_name.$suffix", "expire_file_path" => $new_file_path), true);//创建过期历史文件记录文件
                $ok = rename($file_path, $new_file_path);
                if ($ok) {
                    self::expire($expire);
                }
                return $ok;
            }
        }
        return false;
    }

    /**
     * 添加日志
     * @param string $file_name
     * @param array|string $data
     * @return $this
     */
    public function add($file_name = '', $data = '')
    {
        self::file_expire($file_name, $this->conf['exp']);
        $file_path = rtrim($this->dir_path, '/') . '/';
        if (self::is_string($file_name)) {
            $file_path .= "$file_name.{$this->conf['suffix']}";
            $data = self::data($data)->conf['add_data'];
            if (file_exists($file_path)) {
                $f = fopen($file_path, 'a+');
                fwrite($f, $data);
            } else {
                $top = self::set_head_info('', $file_name);
                $f = fopen($file_path, 'w+');
                fwrite($f, $top . $data);
            }
            fclose($f);
        }
        return $this;
    }

    /**
     * 读取日志数据
     * @param string $file_name
     * @return array|bool
     */
    public function read($file_name = '', $assoc = false)
    {
        $split = array($this->conf['key_split'], $this->conf['row_split']);
        $file_path = trim($this->dir_path, '/') . '/';
        if (self::is_string($file_name)) {
            $file_path .= "$file_name.{$this->conf['suffix']}";
            if (file_exists($file_path)) {
                $str = file_get_contents($file_path);
                $data = explode($split[1], $str);
                $top_data = array_slice($data, 0, 1);
                unset($data[count($data) - 1], $data[0]);
                //读取构造日志数据
                foreach ($data as $k => $v) {
                    $data[$k] = explode($split[0], $v);
                    foreach ($data[$k] as $k1 => $v1) {
                        $v1 = self::text_out(trim($v1));
                        $data[$k][$k1] = $v1;
                        $data[$k][$this->field_list[$k1]] = $v1; // 使用键值访问
                        if ($assoc) {
                            unset($data[$k][$k1]);
                        }
                    }
                }
                $top_data = explode($split[0], $top_data[0]);
                return array("top_data" => unserialize(trim($top_data[0])), "data" => $data);
            }
        }
        return false;
    }

    /**
     * 读取日志主体信息,返回二维数组
     * @param string $file_path
     * @param int $start
     * @param int $leng
     * @param bool $assoc 是否只返回关联数组
     * @return array|bool
     */
    public function get_body_data($file_path = '', $start = 0, $leng = 10, $assoc = false)
    {
        $split = array($this->conf['key_split'], $this->conf['row_split']);
        $start = is_numeric($start) ? intval($start) + 4 : 4;
        $leng = is_numeric($leng) ? intval($leng) : 10;
        $end = $start + $leng - 1;
        $arr = self::file_rows($file_path, $start, $end);
        if (is_array($arr)) {
            foreach ($arr as $index => $item) {
                $item = rtrim($item, $split[1]);
                $item = explode($split[0], $item);
                foreach ($item as $k1 => $v1) {
                    $v1 = self::text_out(trim($v1));
                    $item[$k1] = $v1;
                    $item[$this->field_list[$k1]] = $v1; //使用键值访问
                    if($assoc){
                        unset($item[$k1]);
                    }
                }
                $arr[$index] = $item;
            }
        }
        return $arr;
    }

    /**
     * 读取日志主体列表,返回二维关联数组
     * @param string $file_path
     * @param int $start
     * @param int $leng
     * @return mixed
     */
    public function get_body_data_assoc($file_path = '', $start = 0, $leng = 10)
    {
        $args = func_get_args();
        return call_user_func_array(array($this, "get_body_data"), array($args[0], $args[1], $args[2], true));
    }

    /**
     * 读取文件部分内容,行数从1开始
     * @param string $file_path 文件路径
     * @param int $start 开始行 $start>=1
     * @param int $end 结束行 $end<=0 读完整个文件
     * @param null|int $len 每行读取最大字节数
     */
    public function file_rows($file_path = '', $start = 1, $end = -1, $len = 4096)
    {
        if (self::is_string($file_path)) {
            if (!file_exists($file_path)) {
                $file_path = trim($this->dir_path, '/') . "/$file_path.{$this->conf['suffix']}";
            }
            if (file_exists($file_path)) {
                $row = array();
                $f = fopen($file_path, 'r');
                $line = 1;
                if ($f) {
                    while (!feof($f)) {
                        $str = rtrim(fgets($f, $len));
                        if ($line >= $start) {
                            $str && ($row[] = $str);
                        }
                        if ($end > 0 && $line >= $end) {
                            break;
                        }
                        $line++;
                    }
                }
                fclose($f);
                return $row;
            }
        }
        return false;
    }

    public function str_filter($str, $preg = '')
    {
        $preg || ($preg = "/(^[\r\n\t\s]+|[\r\n\t\s]+$)/i");
        if (is_array($str)) {
            foreach ($str as $index => $item) {
                $str[$index] = preg_replace($preg, '', $item);
            }
        } else {
            $str = preg_replace($preg, '', $str);
        }
        return $str;
    }

    /**
     * @param string|array $data
     * @return array|mixed|string
     */
    public function text_in($data = '')
    {
        if (self::is_string($data)) {
            $re = $this->conf['replace'];
            $data = str_replace(array("\n", $this->conf['key_split'], $this->conf['row_split']), array($re['br'], $re['key_split'], $re['row_split']), $data);
            return $data;
        } elseif (is_array($data)) {
            foreach ($data as $k => $v) {
                $data[$k] = self::text_in($v);
            }
            return $data;
        } else {
            return $data;
        }
    }

    /**
     * @param string|array $data
     * @return array|mixed|string
     */
    public function text_out($data = '')
    {
        if (self::is_string($data)) {
            $re = $this->conf['replace'];
            $data = str_replace(array($re['br'], $re['key_split'], $re['row_split']), array("\n", $this->conf['key_split'], $this->conf['row_split']), $data);
            return $data;
        } elseif (is_array($data)) {
            foreach ($data as $k => $v) {
                $data[$k] = self::text_out($v);
            }
            return $data;
        } else {
            return $data;
        }
    }

    /**
     * 递归浏览目录
     * @param string $dir
     * @param string $root_dir
     * @param bool $previous_dir
     * @return array|bool
     */
    public function read_dir_list($dir = '', $root_dir = '', $previous_dir = false)
    {
        //static $arr = array();
        return self::dir_read($arr, $dir, $root_dir, $previous_dir);
    }

    /**
     * 递归浏览目录
     * @param array $arr
     * @param string $dir 相对路径和以‘/’开头的路径,路径里尽量不要包含 ‘../’
     * @param string $root_dir 根目录,绝对路径,可以忽略
     * @param boolean $previous_dir $dir里路径是否允许包含‘../’
     * @return array|bool
     */
    final private function dir_read(&$arr = array(), $dir = '', $root_dir = '', $previous_dir = false)
    {
        if (self::is_string($dir)) {
            $is_relative = strpos($dir, '/') === 0; //以‘/’开头的路径
            $no_absolute = preg_match("/^(?!([A-Za-z]{1,3}\:))/i", $dir); //不是绝对路径(window系统下)
            if (self::is_string($root_dir) && ($root_dir = realpath($root_dir))) {
                if ($is_relative) {
                    $dir = rtrim($root_dir, '\\/') . $dir;
                } elseif ($no_absolute) {
                    $dir = rtrim($root_dir, '\\/') . "/$dir";
                }
                $self_dir = $root_dir;
            } else {
                if ($is_relative) {
                    if ($_SERVER['DOCUMENT_ROOT']) {
                        $dir = ($self_dir = rtrim($_SERVER['DOCUMENT_ROOT'], '\\/')) . $dir;
                    } else {
                        $dir = ($self_dir = getcwd()) . $dir;
                    }
                } elseif ($no_absolute) {
                    $dir = ($self_dir = getcwd()) . "/$dir";
                }
            }
        } else {
            $self_dir = $dir = self::is_string($root_dir) ? $root_dir : getcwd();
        }
        $previous_dir || ($dir = str_replace(array('../', '..\\'), array('', ''), $dir)); //不允许路径包含‘../’
        $dir = realpath($dir);
        if ($dir) {
            $dir = rtrim(preg_replace("/[\/\\\\]{1,}/", "/", $dir), '/') . '/';
            $self_dir = rtrim(preg_replace("/[\/\\\\]{1,}/", "/", $self_dir), '/'); //相对目录的根目录
            $_dir = rtrim(str_replace($self_dir, '/', $dir), '/') . '/'; //以‘/’开头的相对路径

            $dir_handle = opendir($dir);
            $preg = array("a" => "/[\/\\\\]{1,10}/i");
            while (false !== $list = readdir($dir_handle)) {
                if ($list == '.' || $list == '..') continue;
                $file_path = preg_replace($preg['a'], "/", $dir . $list, -1);
                $relative_path = preg_replace($preg['a'], '/', $_dir . $list, -1);
                if (is_file($file_path)) {
                    $arr['file'][] = array(
                        /*绝对路径(完整的文件路径)*/
                        "path" => $file_path,
                        /*相对路径*/
                        "relative" => $relative_path,
                        /*文件名*/
                        "fileName" => $list,
                        /*文件创建或修改时间*/
                        "filectime" => filectime($file_path),
                        "fctime" => date("Y-m-d H:i:s", filectime($file_path)),
                        /*文件的上次访问时间*/
                        "fileatime" => fileatime($file_path),
                        "fatime" => date("Y-m-d H:i:s", fileatime($file_path)),
                        /*文件的内容上次被修改的时间*/
                        "filemtime" => filemtime($file_path),
                        "fmtime" => date("Y-m-d H:i:s", filemtime($file_path)),
                        /*文件的所有者*/
                        "fileowner" => fileowner($file_path),
                        /*文件的大小 byte(字节)*/
                        "filesize" => filesize($file_path),
                    );
                }
                //判断当前是否为目录
                if (is_dir($file_path)) {
                    //是目录
                    $arr["dir"][] = array(
                        /*完整目录路径(决对路径)*/
                        "path" => $file_path,
                        /*相对路径*/
                        "relative" => $relative_path,
                        /*文件夹(目录)名称*/
                        "dirName" => $list,
                    );
                    self::dir_read($arr, $_dir . '/' . $list, $root_dir);
                }
            }
            closedir($dir_handle);
            return $arr;
        } else {
            return false;
        }
    }

    /**
     * 自定义目录扫描
     * @param string $path
     * @return array|bool
     */
    final public function scandir($path = '')
    {
        if (self::is_string($path) && ($path = realpath($path))) {
            $list = scandir($path);
            $path = rtrim(str_replace('\\', '/', $path), '/') . '/';
            if (is_array($list)) {
                $temp = array("file" => array(), "dir" => array());
                foreach ($list as $val) {
                    if ($val != "." && $val != "..") {
                        $link = $path . $val;
                        $od = array(
                            'path' => $link, 'fileowner' => fileowner($link), 'filesize' => filesize($link), 'filectime' => date('Y-m-d H:i:s', filectime($link)),
                            'fileatime' => date('Y-m-d H:i:s', fileatime($link)), 'filemtime' => date('Y-m-d H:i:s', filemtime($link)),
                        );
                        if (is_dir($link)) {
                            unset($od['fileowner'], $od['filesize']);
                            $temp['dir'][] = array_merge(array('dirName' => $val), $od);
                        }
                        if (is_file($link)) {
                            $temp['file'][] = array_merge(array('fileName' => $val), $od);
                        }
                    }
                }
                return $temp;
            }
        }
        return false;
    }

    /**
     * 判断是否存在
     * @param $var
     * @param array $opt 这里的值代表了不存在的意思
     * @return bool
     */
    static public function exist($var, $opt = array("", null, false))
    {
        isset($var) || ($var = false);
        if (in_array($var, $opt, true)) {
            return false;
        }
        return true;
    }

    /**
     * 判断是否是字符串
     * @param $var
     * @return bool
     */
    static public function is_string($var)
    {
        return self::exist($var) && (is_string($var) || is_numeric($var) || is_double($var));
    }

    /**
     * 浏览器友好的变量输出
     * @param mixed $var 变量
     * @param boolean $echo 是否输出 默认为True 如果为false 则返回输出字符串
     * @param string $label 标签 默认为空
     * @param boolean $strict 是否严谨 默认为true
     * @return void|string
     */
    public function dump($var, $echo = true, $label = null, $strict = true)
    {
        $label = ($label === null) ? '' : rtrim($label) . ' ';
        if (!$strict) {
            if (ini_get('html_errors')) {
                $output = print_r($var, true);
                $output = '<pre>' . $label . htmlspecialchars($output, ENT_QUOTES) . '</pre>';
            } else {
                $output = $label . print_r($var, true);
            }
        } else {
            ob_start();
            var_dump($var);
            $output = ob_get_clean();
            if (!extension_loaded('xdebug')) {
                $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
                $output = '<pre>' . $label . htmlspecialchars($output, ENT_QUOTES) . '</pre>';
            }
        }
        if ($echo) {
            echo($output);
            return null;
        } else
            return $output;
    }

    /**
     * @param int $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
     * @return mixed
     */
    public function ip($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 string $url 页面链接
     * @param int $code 状态码
     */
    final public function url($url = '', $code = 302)
    {
        if (self::is_string($url)) {
            preg_match('/^\//i', $url) && ($url = self::get_url() . $url);
            header('Location: ' . $url, true, $code);
            exit();
        }
    }

    final public function get_url($name = '')
    {
        $data = [];
        $scheme = self::is_ssl() ? 'https://' : 'http://';
        $uri = $_SERVER['PHP_SELF'] . $_SERVER['QUERY_STRING'];
        if ($_SERVER['REQUEST_URI']) {
            $uri = $_SERVER['REQUEST_URI'];
        }
        //首页链接
        $data['host'] = $scheme . $_SERVER['HTTP_HOST'];
        //当前链接
        $data['url'] = $scheme . $_SERVER['HTTP_HOST'] . $uri;
        return $name ? $data[$name] : $data['host'];
    }

    /**
     * 判断是否SSL协议
     * @return boolean
     */
    final private function is_ssl()
    {
        if (isset($_SERVER['HTTPS']) && ('1' == $_SERVER['HTTPS'] || 'on' == strtolower($_SERVER['HTTPS']))) {
            return true;
        } elseif (isset($_SERVER['SERVER_PORT']) && ('443' == $_SERVER['SERVER_PORT'])) {
            return true;
        }
        return false;
    }

    public function encode64($data)
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }

    public function decode64($data)
    {
        return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
    }

    public function is_assoc($arr)
    {
        return array_keys($arr) !== range(0, count($arr) - 1);
    }
    /**
     * 返回毫秒
     * @return float
     */
    public function m_second()
    {
        $t = strval(microtime());
        $l = explode(' ', $t);
        $s = $l[1];
        $ms = ~~(floatval($l[0]) * 1000);
        $v = strval($s) . str_pad(strval($ms), 3, '0', STR_PAD_LEFT);
        return (float)$v;
    }

    /**
     * 返回微秒
     * @return string
     */
    public function u_second()
    {
        $t = strval(microtime());
        $l = explode(' ', $t);
        $s = $l[1];
        $us = ~~(floatval($l[0]) * 1000000);
        $v = strval($s) . str_pad(strval($us), 6, '0', STR_PAD_LEFT);
        return $v;
    }

    /**
     * 返回数组的维度
     * @param $arr
     * @return mixed
     */
    final public function arrayLevel($arr)
    {
        $al = array(0);
        self::aL($arr, $al);
        return max($al);
    }
    static final private function aL($arr, &$al, $level = 0)
    {
        if (is_array($arr)) {
            $level++;
            $al[] = $level;
            foreach ($arr as $k => $v) {
                self::aL($v, $al, $level);
            }
        }
    }
} 


测试文件代码如下:

 <?php
include_once dirname(__FILE__)."/Log.php";
$l = new Log();
/*$lv1_str = $l->handle_data_string(['abc','bbb','ccc'],['abc'=>'abc1','ccc'=>'ccc1','bbb'=>'bbb1']);
$lv2_str = $l->handle_data_string(['x1'=>'abc','x2'=>'ddd','x0'=>'ccc'],[
    ['abc'=>'abc1','ddd'=>'ddd1','ccc'=>'ccc1'],
    ['abc'=>'abc2','ddd'=>'ddd2','ccc'=>'ccc2'],
]);
my_dump($l->handle_string_data(['abc','bbb','ccc'],$lv1_str));
my_dump($l->handle_string_data(['abc','ddd','ccc'],$lv2_str));*/
$log_file = date("Ymd");
//$l->init()->set_dir_path('/log-01/','G:\web\log2233')->data()->add($log_file);
//$l->init()->set('exp', 10)->expire(20)->set_dir_path('/log111')->data()->add($log_file);
$l->init()->set('exp', 20)->expire(20)->set_dir_path('/log111')->data()->add($log_file);
$log_file_path = rtrim($l->get('dir_path'),'/')."/$log_file.log";
$read = $l->read($log_file);
//$l->dump($l->read_dir_list('/','G:\virtual_machine'));exit();
?>
<!DOCTYPE html>
<html>
<head>  <meta charset="utf-8">  <meta http-equiv="X-UA-Compatible" content="IE=edge">  <meta name="Keywords" content="关键词1,关键词2"><!--关键词-->  <meta name="Description" content="描述"><!--描述-->  <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximun-scale=1.0"><!--宽度为设备宽度,初始缩放为1.0倍,最小缩放为1.0倍,最大缩放为1.0倍-->  <!-- 设置浏览器不缓存 begin -->  <meta http-equiv="Pragma" content="no-cache">  <meta http-equiv="Cache-control" content="no-cache">  <meta http-equiv="Cache" content="no-cache">  <!-- 设置浏览器不缓存 end -->  <title></title>
    <style>
        /*表格样式部分 start */
        .table_box{margin:10px 0 0 0;width:100%;border-collapse:collapse;}
        .table_box > thead > tr{height:40px;border:0.5px solid #d5d5d5;box-sizing:border-box;}
        .table_box > thead > tr > th{text-align:center;border:0.5px solid #e2e2e2;box-sizing:border-box;}
        .table_box > tbody > tr{height:35px;}
        .table_box > tbody > tr:nth-of-type(odd){background:#fafafa;}
        .table_box > tbody > tr:hover{background:#f1f1f1;}
        .table_box > tbody > tr > td{padding:0 0 0 5px;border:0.5px solid #e2e2e2;box-sizing:border-box;}
        /*表格样式部分 end */
    </style>
</head>
<body>
    <table class="table_box">
        <thead>
        <tr>
            <th>ip</th>
            <th>添加时间</th>
            <th>地址链接</th>
            <th>请求方式</th>
            <th>http版本</th>
            <th>用户信息(user_gent)</th>
            <th>accept</th>
        </tr>
        </thead>
        <tbody>
        <?php foreach ( $read['data'] as $k => $v): ?>
        <tr>
            <td><?php echos($v['ip']); ?></td>
            <td><?php echos($v['add_time']/*$v[0]*/); ?></td>
            <td><?php echos($v['url']); ?></td>
            <td><?php echos($v['method']); ?></td>
            <td><?php echos($v['protocol']); ?></td>
            <td><?php echos($v['user_agent']); ?></td>
            <td><?php echos($v['accept']); ?></td>
        </tr>
        <?php endforeach; ?>
        </tbody>
    </table>
<?php
//$l->dump($l->get_body_data_assoc($log_file,0,3));
$l->dump($l->history_link($log_file));
?>
</body>
</html> 


图片:


详细代码在我的giee,链接:

https://gitee.com/fang_wen_feng/my_php_plugCode/blob/phpcode/phpCode/log/Log.php

https://gitee.com/fang_wen_feng/my_php_plugCode/blob/phpcode/phpCode/public_functions.php