linux下编译安装php8

下载:

https://www.php.net/downloads

configure

从宝塔安装的php看下通用的一些扩展编译参数
# php -i |grep configure

./configure 
--prefix=/www/server/php/74  --with-config-file-path=/www/server/php/74/etc  --enable-fpm  --with-fpm-user=www  --with-fpm-group=www  --enable-mysqlnd  --with-mysqli=mysqlnd  --with-pdo-mysql=mysqlnd  --with-iconv-dir  --with-freetype  --with-jpeg  --with-zlib  --with-libxml-dir=/usr  --enable-xml  --disable-rpath  --enable-bcmath  --enable-shmop  --enable-sysvsem  --enable-inline-optimization  --with-curl  --enable-mbregex  --enable-mbstring  --enable-intl  --enable-ftp  --enable-gd  --with-openssl  --with-mhash  --enable-pcntl  --enable-sockets  --with-xmlrpc  --enable-soap  --with-gettext  --disable-fileinfo  --enable-opcache  --with-sodium  --with-webp

稍微修改以下

--prefix=/usr/local/php/81 --with-config-file-path=/usr/local/php/81/etc  --enable-fpm  --with-fpm-user=www  --with-fpm-group=www  --enable-mysqlnd  --with-mysqli=mysqlnd  --with-pdo-mysql=mysqlnd  --with-iconv-dir  --with-freetype  --with-jpeg  --with-zlib  --with-libxml-dir=/usr  --enable-xml  --disable-rpath  --enable-bcmath  --enable-shmop  --enable-sysvsem  --enable-inline-optimization  --with-curl  --enable-mbregex  --enable-mbstring  --enable-intl  --enable-ftp  --enable-gd  --with-openssl  --with-mhash  --enable-pcntl  --enable-sockets  --with-xmlrpc  --enable-soap  --with-gettext  --disable-fileinfo  --enable-opcache  --with-sodium  --with-webp

configure跑跑看缺什么就装对应软件

缺少 安装
libxml-2.0 libxml2-dev
libcurl libcurl4-openssl-dev
libpng libpng-dev
libwebp libwebp-dev
libjpeg libjpeg-dev
freetype2 libfreetype-dev
oniguruma libonig-dev
libsodium libsodium-dev

....

安装

执行make && make install

BCC 异或校验 php实现

问题描述

对接一个硬件设备协议要求进行bcc校验,数据包是十六进制表示的,用php处理

实现方式

    public static function dec2hex($data, $wordLength) {
        return str_pad(dechex($data), $wordLength * 2, '0', STR_PAD_LEFT);
    }

    //十六进制数据生成bcc校验码(十六进制)
    public static function bcc($data) {
        $data = str_split($data, 2);
        $length = count($data);
        $result = intval($data[0], 16);
        for($i=0; $i<$length-1; $i++) {
            $result ^= intval($data[$i+1], 16);
        }
        return self::dec2hex($result, 1);
    }

其它

bcc校验工具

dcat admin 多文件上传

问题描述

需要解决dcat admin 表单多附件管理的(附件名和保存文件名分开保存)

解决方案

上传文件管理

  • 首先是新建上传文件表

    Schema::create('uploads', function (Blueprint $table) {
    $table->string('disk', 30)->comment('存储位置');
    $table->string('type', 20)->default('file')->comment('文件类型');
    $table->string('name', 150)->nullable()->comment('文件名称');
    $table->string('path', 150)->comment('文件路径');
    });
  • 新建上传文件用的路由

    $router->any('/upload/handle/{disk}/{dir}', 'UploadController@handle');
  • 上传处理

    public function handle($diskName, $dir) {
        $disk =$this->disk($diskName);
    
        // 判断是否是删除文件请求
        if ($this->isDeleteRequest()) {
            Upload::where('path', request()->key)->delete();
            // 删除文件并响应
            return $this->deleteFileAndResponse($disk);
        }
    
        // 获取上传的文件
        $file = $this->file();
        $newName =  Common::uniqueID().'.'.$file->getClientOriginalExtension();
        $result = $disk->putFileAs($dir, $file, $newName);
        $path = "{$dir}/$newName";
        $upload = new Upload;
        $upload->name = request()->name;
        $upload->path = $path;
        $upload->disk = $diskName;
        $upload->save();
    
        return $result
            ? $this->responseUploaded($path, $disk->url($path))
            : $this->responseErrorMessage('文件上传失败');
    }
  • 扩展字段

    class CustomMultiFile extends MultipleFile
    {
    protected function initialPreviewConfig()
    {
        $previews = [];
        foreach ($this->value() as $path => $name) {
            $previews[] = [
                'id'   => $path,
                'path' => Helper::basename($name),
                'url'  => $this->objectUrl($path),
            ];
        }
    
        return $previews;
    }
    }
  • 注册扩展字段

    Form::extend('customMultiFile', CustomMultiFile::class);
  • 表单字段

    $form->customMultiFile('attachment', '附件')->disk('public')
     ->url('upload/handle/public/attachment')
     ->autoUpload()->autoSave(false)
     ->removable(true)->limit(10);

PHP实现随机红包

    //随机红包(总份数、最大金额、最小金额、总数)
    protected function random_redpaper($total, $max, $min, $sum){
        $sum = $sum * 100;
        //按分单位,后面全部用整数金额

        $users = array_fill(0, $total, $min);
        $left = $sum - $min * $total;

        //将剩余金额按rand(1, ceil($left/$total))每次进行随机分配, $left 小于50直接分配完成
        for(;$left > 0;){
            $amount = $left > 50 ? rand(1, ceil($left/$total)) : $left;
            $user_index = rand(0, $total-1);
            if($users[$user_index] + $amount > $max){
                $amount = $max - $users[$user_index];
                $users[$user_index] = $max;
            }else{
                $users[$user_index] += $amount;
            }
            $left -= $amount;
        }

        //恢复按元单位
        foreach($users as &$user){
            $user = $user/100;
        }
        return $users;
    }

Laravel model 使用trait+boot初始化

目标

用trait来给一些model添加通用的boot方法,加上相同的globalscope

解决方法

试了一下,直接在trait里面写boot方法是没用的,然后找到了这个问答

https://laracasts.com/discuss/channels/eloquent/cant-use-trait-if-i-have-a-boot-method-declared

原来源码里已经写好了这个调用了,搜索一下model里面的boot方法

/**
     * Bootstrap the model and its traits.
     *
     * @return void
     */
    protected static function boot()
    {
        static::bootTraits();
    }

    /**
     * Boot all of the bootable traits on the model.
     *
     * @return void
     */
    protected static function bootTraits()
    {
        $class = static::class;

        $booted = [];

        static::$traitInitializers[$class] = [];

        foreach (class_uses_recursive($class) as $trait) {
            $method = 'boot'.class_basename($trait);

            if (method_exists($class, $method) && ! in_array($method, $booted)) {
                forward_static_call([$class, $method]);

                $booted[] = $method;
            }

            if (method_exists($class, $method = 'initialize'.class_basename($trait))) {
                static::$traitInitializers[$class][] = $method;

                static::$traitInitializers[$class] = array_unique(
                    static::$traitInitializers[$class]
                );
            }
        }
    }

哈哈,原来框架已经实现好了,感叹一下确实想的太周到了哈!!
只要在trait里面写
'boot'.class_basename($trait)
也就是boot+trait名字的方法就会被调用了。

PHP常量表的实现

实际项目过程,经常遇到一些键值组成的常量表,今天想了一下怎么用比较好的方式实现这种结构。

  • 一个想法是建一个常量表,通过数据库来管理常量,这个实现比较直接,就不展开了。

  • 另一个想法是把这种结构用类来表示。

abstract class Constants{
    static $map = [];
    public static function getKey($value){
        return !empty($key = array_search($value, static::$map)) ? $key : null;
    }

    public static function getValue($key){
        return isset(static::$map[$key])? static::$map[$key] : null;
    }

    public static function getMap(){
        return static::$map;
    }
}

class Phone extends Constants{
    static $map = [
        10086 => '移动',
        10000 => '电信'
    ];

}

echo Phone::getValue(10000), PHP_EOL;
echo Phone::getKey('电信'), PHP_EOL;

这里本来Constants 这个类本来打算设置一个$map的abstract的属性,但是却报错了,属性不支持定义为abstract。

然后一顿搜索,找到了一个替代方案

abstract class Foo_Abstract {
  abstract public function get_tablename();
}

class Foo extends Foo_Abstract {
  protected $tablename = 'tablename';
  public function get_tablename() {
    return $this->tablename;
  }
}

不过这样代码会多很多,所以感觉用上面的实现也足够了吧。

laravel sanctum 错误自定义返回

现象

sanctum 认证失败会自动返回401或419 errercode,
前端认证逻辑这里会受到影响,所以希望可以自定义错误返回。

解决方法

https://laravel.com/docs/8.x/errors#renderable-exceptions

在Exception/Handler 里面定义对应AuthenticationException 的渲染方法

    public function register()
    {
        $this->renderable(function (AuthenticationException $e) {
            return [
                'error' => 403,
                'message' => "未通过身份验证"
                ];
        });
    }