coding code
change world

composer install memo

卢哥阅读(83)

composer 安装备忘 以后省的每次都要去官网找 在自己博客找方便一些

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
mv composer.phar /usr/local/bin/composer
php -r "unlink('composer-setup.php');"
使用阿里云镜像:
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

自己动手用PHP撸一个简单版的HTTP Server

卢哥阅读(64)

HTTP协议我的简化版理解就是电脑上浏览器向服务器发送一个预先定义好的文本(Http Request)
然后服务器端处理一下(通常是从硬盘读取一个后缀名为html的文件),然后再把这个文件
通过文本方式发回去(Http Response),就这么简单。

唯一麻烦的是我得请操作系统给我建立Http层下面的TCP连接通道,因为所有的文本数据都得
通过TCP管道接收和发送,这个通道是用socket建立的。

伪代码

socketMain= socket(...)
bind(socketMain,主机的IP和端口号)
listen(socketMain,...)

无限循环
while(true) {
    socketAccept = accept(socketMain,....)
    receive(socketAccept,....)
    send(socketAccept...)
    close(socketAccept...)

}

这些socket,bind,listen,accept都是操作系统提供的接口,我们要做的就是把这些进行
组装;现在80或者其他端口监听,然后进入无限循环,如果有请求进来,就接受(accept),创建新的socket,最后通过这个socket来接收和发送Http数据。

<?php


set_time_limit(0);

class HttpServer
{
    private $ip = '127.0.0.1';
    private $port = 9996;

    private $_socket = null;

    public function __construct()
    {
        $this->_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if ($this->_socket === false) {
            die(socket_strerror(socket_last_error($this->_socket)));
        }
    }

    public function run()
    {
        socket_bind($this->_socket, $this->ip, $this->port);
        socket_listen($this->_socket, 5);
        while(true) {
            $socketAccept = socket_accept($this->_socket);
            $request = socket_read($socketAccept, 1024);
            echo $request;
            socket_write($socketAccept, 'HTTP/1.1 200 OK'.PHP_EOL);
            socket_write($socketAccept, 'Date:'.date('Y-m-d H:i:s').PHP_EOL);

            $fileName = $this->getUri($request);
            $fileExt = preg_replace('/^.*\.(\w+)$/', '$1', $fileName);
            $fileName = __DIR__.'/'.$fileName;
            switch ($fileExt) {
                case "html":
                    //set content type
                    socket_write($socketAccept, 'Content-Type: text/html'.PHP_EOL);
                    socket_write($socketAccept, ''.PHP_EOL);
                    $fileContent = file_get_contents($fileName);
                    socket_write($socketAccept, $fileContent, strlen($fileContent));
                    break;
                case "jpg":
                    socket_write($socketAccept, 'Content-Type: image/jpeg'.PHP_EOL);
                    socket_write($socketAccept,''.PHP_EOL);
                    $fileContent = file_get_contents($fileName);
                    socket_write($socketAccept, $fileContent, strlen($fileContent));
                    break;
            }
            socket_write($socketAccept, 'web serving', strlen('web serving'));
            socket_close($socketAccept);

        }

    }

    protected function getUri($request = '')
    {
        $arrayRequest = explode(PHP_EOL, $request);
        $line = $arrayRequest[0];
        $file = trim(preg_replace('/(\w+)\s\/(.*)\sHTTP\/1.1/i','$2', $line));
        return $file;
    }


    public function close()
    {
        socket_close($this->_socket);
    }





}
$httpServer = new HttpServer();
$httpServer->run();

PHP中如何主动触发error以及如何捕捉error并自定义处理函数

卢哥阅读(68)

php中的错误抛出及自定义处理指的是允许我们自定义抛出错误并且捕捉错误并进行处理。类似于

面向对象语言中的throw 及 try…catch…

在php中主要由以下三个函数来完成抛出以及捕捉处理.

trigger_error()

set_exception_handler()

set_error_handle()

trigger 简介
在php中,trigger函数可以触发任一用户级别的错误,在非面向对象编程中,可以做为一种错误提示的方式。

例如:我们编写一个e($n)的函数,而参数必须是一个整数,否则触发错误

function e($n)
{
  if (!is_int($n)) {
      trigger_error("n is not a number", E_USER_NOTICE);
  }
}
e('this is a string');

自定义错误处理函数

function myErrorHandler($errno, $errstr, $errfile, $errline)
{
    if (!(error_reporting() & $errno)) {
        // This error code is not included in error_reporting
        return;
    }

    switch ($errno) {
        case E_USER_ERROR:
            echo "<b>My ERROR</b> [$errno] $errstr<br />\n";
            echo "  Fatal error on line $errline in file $errfile";
            echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
            echo "Aborting...<br />\n";
            exit(1);
            break;

        case E_USER_WARNING:
            echo "<b>My WARNING</b> [$errno] $errstr<br />\n";
            break;

        case E_USER_NOTICE:
            echo "<b>My NOTICE</b> [$errno] $errstr<br />\n";
            break;

        default:
            echo "Unknown error type: [$errno] $errstr<br />\n";
            break;
    }

    /* Don't execute PHP internal error handler */
    return true;
}

注册该函数为PHP错误处理函数

set_error_handler('myErrorHandler')

Swoole简单几行代码实现终端心跳检测

卢哥阅读(74)

代码逻辑

  • 监听本机9501端口
  • 设置心跳检测频率、heartbeat_idle_time
  • 有终端连接上来,记录远程IP和端口
  • 终端发送自己的终端编号,前缀+终端编号作为key,value为1存入redis,该终端已经上线
  • 连接关闭,删除redis中对应的key,判定该终端已经上线
  • nohup php artisan terminal:startHeartServer&
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class TerminalHeartServerCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'terminal:startHeartServer';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '启动终端心跳服务器';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
    	$buffers = [];
		// 创建Server对象,监听 127.0.0.1:9501端口
		$serv = new \Swoole\Server(env('TERMINAL_HEART_SERVER_IP'), env('TERMINAL_HEART_SERVER_PORT'));

		// 监听连接进入事件
		$serv->on('Connect', function ($serv, $fd) use ($buffers){
			$client_info = $serv->getClientInfo($fd);
			$buffers[$fd]['ip'] = $client_info['remote_ip'];
			Log::channel('terminal_heart')->info('Connect:' . $client_info['remote_ip'] . ':' . $client_info['remote_port']);
		});

		// 监听数据接收事件
		$serv->on('Receive', function ($serv, $fd, $from_id, $data) use ($buffers){
			Redis::set('terminal_heart_' . $data, 1);
			$buffers[$fd]['token'] = $data;
			Log::channel('terminal_heart')->info('Receive:' . 'From: '. $buffers[$fd]['ip'] . ', Token:' . $data);
		});

		// 监听连接关闭事件
		$serv->on('Close', function ($serv, $fd) use ($buffers){
			Redis::del('terminal_heart_' . $buffers[$fd]);
			$client_info = $serv->getClientInfo($fd);
			Log::channel('terminal_heart')->info('Close: IP: ' . $client_info['remote_ip'] . ', Token:' . $buffers[$fd]['token'] );
		});

		$serv->set(array(
			'heartbeat_check_interval' => env('TERMINAL_HEARTBEAT_CHECK_INTERVAL'), // X秒检测一次
			'heartbeat_idle_time' => env('TERMINAL_HEARTBEAT_IDLE_TIME') // 超过X秒就认为对方已经挂了
		));

		//启动服务器
		$serv->start();
	}
}

Warning: require(): open_basedir restriction in effect 如何解决

卢哥阅读(75)


PHP message: PHP Warning:  Unknown: failed to open stream: Operation not permitted in Unknown on line 0
Unable to open primary script: /home/wwwroot/www.luyunhua.work/index.php (Operation not permitted)" while reading response header from upstream, client: 183.128.125.125, server: www.luyunhua.work, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/tmp/php-cgi.sock:", host: "www.luyunhua.work"
2020/06/21 22:15:18 [error] 18945#0: *11543 FastCGI sent in stderr: "PHP message: PHP Warning:  Unknown: open_basedir restriction in effect. File(/home/wwwroot/www.luyunhua.work/index.php) is not within the allowed path(s): (/home/wwwroot/api.okpay/public/:/tmp/:/proc/) in Unknown on line 0
PHP message: PHP Warning:  Unknown: failed to open stream: Operation not permitted in Unknown on line 0

1、修改php.ini文件中的open_basedir

open_basedir = /home/wwwroot/:/tmp/:/proc/

/home/wwwroot/ 一般为所有项目的root目录,自行替换成自己的根目录即可,这样php就能够访问到/home/wwwroot/目录以及其子目录的所有文件

2、修改fastcgi.conf

未修改前:

fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;
fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/";

变更点在 最后一句注释了

这句的含义是:nginx会通过fastcgi向本地的php运行环境传递这个参数,告诉php,我这次的PHP程序只能打开如上我定义的目录啊,其他的不可以

修改后:

fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;
# fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/";

发生原因详解

在Thinkphp、codeigniter、Laravel等框架下,网站目录一般是在public下,但是public下的程序要跨目录调用public上级目录下的文件,

例如: composer 自动产生的vendor文件夹,public 上层目录中的.env文件以及一些文件存储以及日志文件

而这些文件或者文件夹都跟public目录是处于同一级,并不是public的子目录

分享一个简单的mysql数据库备份脚本

卢哥阅读(99)




#!/bin/bash
# Shell script to backup MySQL database

# Set these variables
MyUSER=""	# DB_USERNAME
MyPASS=""	# DB_PASSWORD
MyHOST="localhost"	# DB_HOSTNAME

# Backup Dest directory
DEST="/mysql_backup" # /home/username/backups/DB


# How many days old files must be to be removed
DAYS=5

# Linux bin paths
MYSQL="$(which mysql)"
MYSQLDUMP="$(which mysqldump)"
GZIP="$(which gzip)"

# Get date in dd-mm-yyyy format
NOW="$(date +"%Y-%m-%d_%H-%M-%S")"

# Create Backup sub-directories
MBD="$DEST/$NOW/mysql"
install -d $MBD

# DB skip list
SKIP="information_schema
mysql
performance_schema
sys
"

# Get all databases
DBS="$($MYSQL -h $MyHOST -u $MyUSER -p$MyPASS -Bse 'show databases')"

# Archive database dumps
for db in $DBS
do
    skipdb=-1
    if [ "$SKIP" != "" ];
    then
        for i in $SKIP
        do
            [ "$db" == "$i" ] && skipdb=1 || :
        done
    fi
 
    if [ "$skipdb" == "-1" ] ; then
    	FILE="$MBD/$db.sql"
    $MYSQLDUMP -h $MyHOST -u $MyUSER -p$MyPASS $db --default-character-set=utf8 > $FILE
    fi
done

# Archive the directory, send mail and cleanup
cd $DEST
tar -cf $NOW.tar $NOW
$GZIP -9 $NOW.tar

echo "MySQL backup is completed! Backup name is $NOW.tar.gz"
rm -rf $NOW

# Remove old files
find $DEST -mtime +$DAYS -exec rm -f {} \;