标签归档:php

laravel-websockets使用教程

后端

1、安装扩展

composer require beyondcode/laravel-websockets

发布迁移

php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="migrations"
php artisan migrate

发布配置

php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config"

配置 .env

PUSHER_APP_ID=
PUSHER_APP_KEY=your-pusher-key
PUSHER_APP_SECRET=
PUSHER_HOST=127.0.0.1
PUSHER_PORT=6001
PUSHER_SCHEME=http
PUSHER_APP_CLUSTER=mt1

2、确保 config/app.php 文件,providers 配置中没有注释依赖项。如果有不能解释的报错,可以确认一下。

3、创建事件

php artisan make:event DemoEvent

修改类 App\Events\DemoEvent ,注意添加实现 ShouldBroadcast

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class DemoEvent implements ShouldBroadcast # 添加实现类
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public string $message;

    // 使用成员变量,前端 listen 事件名为 `DemoEvent`
    // public $broadcastAs = 'DemoEvent';

    public function __construct()
    {
        $this->message = 'Hello friend';
    }

    // 使用成员方法,前端 listen 事件名为 `.DemoEvent`
    public function broadcastAs() {
        return 'DemoEvent';
    }

    public function broadcastOn()
    {
        // PrivateChannel 需要登录,改为 Channel
        // channel 名称本文改为 public
        return new Channel('public');
    }
}

如果不指定 broadcastAs,默认事件名为类名,即: App\Events\DemoEvent

4、运行服务

php81 artisan websockets:serve

5、调用/下发事件

# 以下都行
DemoEvent::dispatch();
broadcast(new DemoEvent())->toOthers();
broadcast(new DemoEvent());
event(new DemoEvent());

6、DASHBOARD

访问 /laravel-websockets 可以打开 dashboard 和调试,地址可以在 config/websockets.php 中配置

前端

1、以 vue 为例,安装扩展

npm i -D laravel-echo pusher-js

2、添加 laravel-echo.js 文件

import Echo from "laravel-echo";
import Pusher from "pusher-js";
window.Pusher = Pusher;

window.Echo = new Echo({
  broadcaster: "pusher",
  key: "your-pusher-key",
  wsHost: window.location.hostname,
  wsPort: 6001,
  forceTLS: false,
  disableStats: true,
  cluster: "mt1",
});

main.js 中引入

import "./laravel-echo";

3、在合适的位置订阅消息

window.Echo.channel("public").listen(".DemoEvent", (e) => {
  console.log(e.message);
});

此处 DemoEvent 为事件名,在后端可以通过 broadcastAs 来指定:

  1. 如果没有指定,默认为类名: App\\Events\\DemoEvent
  2. 如果使用成员变量:DemoEvent
  3. 如果使用成员方法:.DemoEvent
继续阅读

php 框架执行存储过程(stored procedure) 并获取返回

laravel

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use PDO;

class TestController extends Controller
{
    public function index()
    {
        $pdo = DB::getPdo();
        $int = 1;
        $res = 0;
        $stmt = $pdo->prepare("EXEC dbo.test :int,:res");
        $stmt->bindParam(':int', $res, PDO::PARAM_INT);
        $stmt->bindParam(':res', $res, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 400);
        $stmt->execute();
        dd($res);
    }
}

参考:

  • https://stackoverflow.com/questions/71321858/api-laravel-call-store-procedure-with-in-and-out-parameter-using-oci8-or-pdo
  • https://laracasts.com/discuss/channels/general-discussion/running-stored-procedures

thinkphp 5.0

tp5 需要修改原码才可以使用:

  • 找到文件 thinkphp/library/think/db/Connection.php
  • 第 388 行,在 catch (\PDOException $e) 内,修改为如下代码:
    if ($procedure == true) { return; } elseif ($this->isBreak($e)) { return $this->close()->query($sql, $bind, $master, $pdo); }

控制器执行:

<?php

namespace app\api\controller;

use PDO;
use think\Db;

class Test extends Common {
    public function index() {
        $int = 1;
        $res = 1;
        Db::query('exec test :int,:res', [
            'int' => $int,
            'res' => [&$res, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 4000],
        ]);
        print_r($res);
    }
}

参考:

  • https://blog.csdn.net/Drug_/article/details/95474776

以上示例使用的存储过程如下:

ALTER PROC [dbo].[test]
-- 创建:CREATE PROC [dbo].[test]
 @IntInput int,
 @StrResult varchar(20) out
as
begin
         if (@IntInput>1)
           Set
        @StrResult = '>1'
        else 
            Set
        @StrResult = '<=1'
end

SQL 执行:

declare @strResult varchar(20)
exec test -1,@strResult output
print @strResult

ubuntu 安装 php 扩展

sudo apt-get install php-dev php-pear build-essential libaio1
sudo pecl install oci8 # 出现提示直接回车
sudo echo "extension=oci8.so" >> /etc/php/7.x/mods-available/oci8.ini # 注意修改目录(php版本号),文件权限应为 `644`
sudo phpenmod oci8
sudo service apache2 restart

或直接下载对应编译版本,解压后放到 /usr/lib/php/编译日期/ 目录下(文件权限 644): php/20151012php/20160303php/20170718

lumen 使用 redis

安装扩展

composer require predis/predis
composer require illuminate/redis

引入redis支持

$app->register(Illuminate\Redis\RedisServiceProvider::class);

启用redis辅助函数

$app->withFacades();
$app->withEloquent();

配置redis服务器参数

默认系统是调用的.env里的redis配置文件,但是一般安装后没有这些参数,可以查看文件路径vendor/laravel/lumen-framework/config/database.php中查看有哪些参数需要配置,例如,我的.env文件需要配置

REDIS_HOST=192.168.1.41
REDIS_PORT=7000
REDIS_PASSWORD=123456

CentOS7 nginx+php-fpm 环境安装

SELinux

  1. 查看
    1. getenforce/usr/sbin/sestatus -v
    2. Enforcing 是启用,Disabled 是禁用
  2. 永久关闭
    1. 编辑文件 /etc/selinux/config
    2. SELINUX=enforcing 改为 SELINUX=disabled
    3. 重启

启用网络

  1. 查看网络连接:ip addr
  2. 编辑文件 /etc/sysconfig/network-scripts/ifcfg-ens33ens33 是网络名称)

  3. ONBOOT=no 改为 ONBOOT=yes

  4. 使用静态IP(可跳过)

    1. 修改 BOOTPROTO=dhcpBOOTPROTO=static
  5. 追加以下代码(使用自己的IP信息)

    IPADDR=192.168.1.200
    NETMASK=255.255.255.0
    GATEWAY=192.168.1.1
    DNS1=114.114.114.114
    DNS2=8.8.8.8
    

  • 重启网络服务 systemctl restart network
  • 继续阅读

    PHP APC缓存配置、使用详解

    一、APC缓存简介

    APC,全称是Alternative PHP Cache,官方翻译叫”可选PHP缓存”。它为我们提供了缓存和优化PHP的中间代码的框架。 APC的缓存分两部分:系统缓存和用户数据缓存。

    系统缓存

    它是指APC把PHP文件源码的编译结果缓存起来,然后在每次调用时先对比时间标记。如果未过期,则使用缓存的中间代码运行。默认缓存

    3600s(一小时)。但是这样仍会浪费大量CPU时间。因此可以在php.ini中设置system缓存为永不过期 apc.ttl=0。不过如果这样设置,改运php代码后需要重启WEB服务器。目前使用较多的是指此类缓存。

    用户数据缓存

    缓存由用户在编写PHP代码时用apc_store和apc_fetch函数操作读取、写入的。如果数据量不大的话,可以一试。如果数据量大,使用类似memcache此类的更加专著的内存缓存方案会更好

    缓存key生成规则

    APC的缓存中的每个slot都会有一个key,key是

    apc_cache_key_t结构体类型,除了key相关的属性,关键是h字段的生成。 h字段决定了此元素落于slots数组的哪一个位置。对于用户缓存和系统缓存,其生成规则不同。 用户缓存通过apc_cache_make_user_key函数生成key。通过用户传递进来的key字符串,依赖PHP内核中的hash函数(PHP的hashtable所使用的hash函数:zend_inline_hash_func),生成h值。

    系统缓存通过apc_cache_make_file_key函数生成key。通过APC的配置项apc.stat的开关来区别对待不同的方案。在打开的情况下,即

    apc.stat= On 时,如果被更新则自动重新编译和缓存编译后的内容。此时的h值是文件的device和inode相加所得的值。在关闭的情况下,即apc.stat=off时,当文件被修改后,如果要使更新的内容生效,则必须重启Web服务器。此时h值是根据文件的路径地址生成,并且这里的路径是绝对路径。即使你是使用的相对路径,也会查找PG(include_path)定位文件,以取得绝对路径,所以使用绝对路径会跳过检查,可以提高代码的效率。

    添加缓存过程

    以用户缓存为例,apc_add函数用于给APC缓存中添加内容。如果key参数为字符串中,APC会根据此字符串生成key,如果key参数为数组,APC会遍历整个数组,生成key。根据这些key,APC会调用_apc_store将值存储到缓存中。由于这是用户缓存,当前使用的缓存为apc_user_cache。执行写入操作的是apc_cache_make_user_entry函数,其最终调用apc_cache_user_insert执行遍历查询和写入操作。与此对应,系统缓存使用apc_cache_insert执行写入操作,其最终都会调用_apc_cache_insert。

    不管是用户缓存还是系统缓存,大体的执行过程类似,步骤如下:

    通过求余操作,定位当前key的在slots数组中的位置: cache->slots[key.h % cache->num_slots];

    在定位到slots数组中的位置后,遍历当前key对应的slot链表,如果存在slot的key和要写入的key匹配或slot过期,清除当前slot。

    在最后一个slot的后面插入新的slot。

    二、APC模块安装

    A.WINDOWS下安装APC

    第一步:下载php_apc.dll 在 http://pecl.php.net/package/apc 要与php版本对应 将php_apc.dll放入你的ext目录

    第二步:让php.ini支持apc扩展模块。 然后打开php.ini 加入:

    extension=php_apc.dll
    apc.rfc1867 = on
    apc.max_file_size = 100M
    upload_max_filesize = 100M
    post_max_size = 100M
    // 以上参数可自己定义
    

    第三步:检查是否支持PHP APC apc_store apc_fetch

    查看phpinfo中是否有apc相关项

    B.LIUNX下安装APC

    第一步:下载和安装

    wget http://pecl.php.net/get/APC-3.1.8.tgz
    tar -zxvf APC-3.1.8.tgz cd APC-3.1.8
    /usr/local/php/bin/phpize
    ./configure --enable-apc --enable-mmap --enable-apc-spinlocks --disable-apc-pthreadmutex --with-php-config=/usr/local/php/bin/php-config
    make
    sudo make install
    

    第二步:配置APC

    在/usr/local/php/etc/php.ini 加入以下配置项:

    extension = "apc.so" ;
    ;APC setting
    apc.enabled = 1
    apc.shm_segments = 1
    apc.shm_size = 64M
    apc.optimization = 1
    apc.num_files_hint = 0
    apc.ttl = 0
    apc.gc_ttl = 3600
    apc.cache_by_default = on
    

    第三步:检查安装是否成功

    重启apache 或者 /usr/local/php/sbin/php-fpm restart

    查看phpinfo中是否有apc相关项

    三、配置参数详解和使用总结

    1).APC模块的参数配置详解

    apc.enabled 布尔型

    apc.enabled 可以被设成 0 来禁用 APC。这主要是有用的,当 APC 被静态编译入 PHP 时,因为没有其它方法来禁用它(当编译为 DSO 的时候,可以将 php.ini 中的 extension 行注释掉)。

    apc.shm_segments 整型

    对编译缓存分配共享内存块的数量。如果APC用光了共享内存,而且你已经设置apc.shm_size为系统允许的最大值的情况下,你可以试着去提高这个参数的值。

    apc.shm_size 整型

    每个共享内存块的大小是以MB为单位的。在默认情况下,一些系统(包括大多数BSD变种系统)的共享内存块的大小限制的很低。

    apc.optimization 整型

    优化等级。设为0则禁用优化,越高的值使用越强有力的优化。期待有适度的速度上的改进。这个还是实验性质的。

    apc.num_files_hint 整型

    对在你的Web服务器上被包含和请求的不同的源文件的数量的提示。如果你无法确定,设置为0或者省略;这个设置主要可能用于有成千的源文件的站点。

    apc.ttl 整型

    当一个缓存条目在缓存区的位置被另一个条目需要时,我们需要考虑的是这个缓存条目在缓存区的位置被允许空闲的秒数。将这个参数设置为0意味着你的缓存可能充满不新鲜的条目,同时导致新的条目无法被缓存。

    apc.gc_ttl 整型

    缓存条目在垃圾收集列表中存活的秒数。这个值提供了出错保护在执行一个缓存源文件,而同时服务器进程死了的事件中。如果那个源文件被修改,内存分配给旧版本的缓存条目将不会被回收,直到这个参数设定的TTL值到的时候。设置为0就是禁止这个特性。

    apc.cache_by_default 布尔型

    默认为On,但可以被设置为Off并和以加号开头的apc.filters配合使用,文件仅仅在匹配过滤器时才被缓存。

    apc.filters 字符串

    一个以逗号分割的POSIX扩展正则表达式的列表。如果任何模式匹配源文件名,这个文件将不会被缓存。注意用来匹配的文件名是传递给 include/require 的文件名,而不是绝对路径。如果正则表达式的第一个字符是 + ,则这个表达式就意味着任何匹配表达式的文件将会被缓存,如果第一个字符是 – 则任何匹配都不会被缓存。 – 是默认值,所以可以被省略。

    apc.mmap_file_mask 字符串 (这段实在不太懂,所以没有翻译)

    If compiled with MMAP support by using –enable-mmap this is the mktemp-style file_mask to pass to the mmap module for determing whether your mmap’ed memory region is going to be file-backed or shared memory backed. For straight file-backed mmap, set it to something like/tmp/apc.XXXXXX (exactly 6 Xs). To use POSIX-style shm_open/mmap put a .shm somewhere in your mask. e.g. /apc.shm.XXXXXX You can also set it to /dev/zero to use your kernel’s/dev/zero interface to anonymous mmap’ed memory. Leaving it undefined will force an anonymous mmap.

    apc.slam_defense 整型

    在非常繁忙的服务器上,无论你启动服务还是修改文件,你都会导致一种多进程都试图在同一个时间缓存同一个文件的竞争。这个选项设置了进程跳过试图去缓存一个未被缓存的文件的百分比。或者可以把这个想象成一个单独进程跳过缓存的机率。例如,设置apc.slam_defense为75就意味着进程有75%的机率不去缓存未被缓存的文件。所以,设置的越高,越能减少缓存的碰撞机率。设置为0则禁用这个特性。

    apc.file_update_protection 整型

    当你在一个运行着的服务器上修改文件时,你应该执行原子操作。也就是,先写一个临时文件,当写完后再重命名(mv)这个文件到它的最终位置。许多文本编辑器,cp,tar和其他一些类似程序都不是这样操作的。这就意味着有机会去访问和(缓存)文件,当这个文件还在被写的情况下。apc.file_update_protection的设置使得缓存标记新文件的延迟。默认值是2,意味着如果发现文件的修改时间距离访问时间不到2秒,文件将不会被缓存。访问写到一半的文件的不幸用户将会看到离奇的情况,但至少这种情况不是持续的。如果你确信你经常使用原子操作来更新你的文件,你可以关闭这个保护通过设置这个参数为0。如果你的系统充满io操作,并导致更新程序花费超过2秒,你可能需要去增大这个值。

    apc.enable-cli 整型

    大多是为了测试和调试。

    为CLI版本的PHP开启动APC功能。一般来说,将不会想到为每一个CLI请求创建,移植和放弃APC的缓存,但对于各种测试情况,这是很容易的为了CLI版本开启APC。

    2).使用总结

    1,使用Spinlocks锁机制,能够达到最佳性能。

    2,APC提供了apc.php,用于监控与管理APC缓存。不要忘记修改管理员名和密码

    3,APC默认通过mmap匿名映射创建共享内存,缓存对象都存放在这块”大型”的内存空间。由APC自行管理该共享内存

    4,我们需要通过统计调整apc.shm_size、apc.num_files_hints、apc.user_entries_hint的值。直到最佳

    5,好吧,我承认apc.stat = 0 可以获得更佳的性能。要我做什么都可以接受.

    6,PHP预定义常量,可以使用apc_define_constants()函数。不过据APC开发者介绍说pecl hidef性能更佳,抛异define吧,它是低效的。

    7,函数apc_store(),对于系统设置等PHP变量,生命周期是整个应用(从httpd守护进程直到httpd守护进程关闭),使用APC比Memcached会更好。必竟不要经过网络传输协议tcp。

    8,APC不适于通过函数apc_store()缓存频繁变更的用户数据,会出现一些奇异现象。

    四、使用实例

    引用initphp框架的APC缓存类

    <?php 
    if 
    class Apc{ 
        /**
         * Apc缓存-设置缓存
         * 设置缓存key,value和缓存时间
         * @param  string $key   KEY值
         * @param  string $value 值
         * @param  string $time  缓存时间
         */ 脚本学堂 http://www.jbxue.com
        public function set_cache($key, $value, $time = 0) {  
            if ($time == 0) $time = null; //null情况下永久缓存 
            return apc_store($key, $value, $time);; 
        } 
    
        /**
         * Apc缓存-获取缓存
         * 通过KEY获取缓存数据
         * @param  string $key   KEY值
         */ 
        public function get_cache($key) { 
            return apc_fetch($key); 
        } 
        /**
         * Apc缓存-清除一个缓存
         * 从memcache中删除一条缓存
         * @param  string $key   KEY值
         */ 
        public function clear($key) { 
            return apc_delete($key); 
        } 
        /**
         * Apc缓存-清空所有缓存
         * 不建议使用该功能
         * @return
         */ 
        public function clear_all() { 
            apc_clear_cache('user'); //清除用户缓存 
            return apc_clear_cache(); //清楚缓存 
        } 
        /**
         * 检查APC缓存是否存在
         * @param  string $key   KEY值
         */ 
        public function exists($key) { 
            return apc_exists($key); 
        } 
        /**
         * 字段自增-用于记数
         * @param string $key  KEY值
         * @param int    $step 新增的step值
         */ 
        public function inc($key, $step) { 
            return apc_inc($key, (int) $step); 
        } 
        /**
         * 字段自减-用于记数
         * @param string $key  KEY值
         * @param int    $step 新增的step值
         */ 
        public function dec($key, $step) { 
            return apc_dec($key, (int) $step); 
        } 
        /**
         * 返回APC缓存信息
         */ 
        public function info() { 
            return apc_cache_info(); 
        } 
    }