首页
关于
Search
1
2022最新WPS政府/教育版合集
3,282 阅读
2
欢迎使用 Typecho
3,100 阅读
3
thinkphp 做301重定向跳转
2,550 阅读
4
IDE 注册教程
2,382 阅读
5
nginx反向代理设置泛目录解析
2,372 阅读
源码
教程
HTML
JAVASCRIPT
PHP
MYSQL
系统
LINUX
WINDOWS
填坑
工具
登录
/
注册
Search
标签搜索
系统工具
内网穿透
PHPDesktop
TaurusCoders
累计撰写
52
篇文章
累计收到
1
条评论
首页
栏目
源码
教程
HTML
JAVASCRIPT
PHP
MYSQL
系统
LINUX
WINDOWS
填坑
工具
页面
关于
搜索到
27
篇与
PHP
的结果
2024-04-02
PhpStorm配置Xdebug最完整最详解教程
前言很多人好奇网上关于php配置xdebug的教程那么多,为什么我还要写这篇文章?因为网上的教程配置很乱,很多都是复制粘贴,有些配置项根本不需要,也不解释干嘛的,而且不够详细。本教程主要应用于Windows,Mac和Linux基本上同理。配置xdebug扩展只配置Debug,不配置profiler和trace,profiler和trace干嘛的请百度。因为大多数情况下是单用户调试,所以不需要配置多用户参数,如:php.ini文件xdebug.idekey参数和PhpStorm的Debug -> DBGp Proxy。所以php.ini大概配置如下:zend_extension= php_xdebug.dllxdebug.remote_enable=Onxdebug.remote_host=127.0.0.1xdebug.remote_port=9100xdebug官网下载地址:https://xdebug.org/download.php扩展一定要装对,注意区分ts和nts版本,phpinfo()查看扩展是否成功。推荐 phpEnv集成环境 ,自带xdebug扩展和多版本php设置PhpStorm打开PhpStorm,菜单File -> Settings,然后找到 Languages & Frameworks -> PHP -> Debug设置Debug port为9100为什么不是9000?因为会和一些集成环境的php-cgi或者php-fpm端口冲突.然后配置Languages & Frameworks -> PHP -> Servers然后找到PhpStorm右上角电话图标,开始监听php debug的链接打开 phpEnv集成环境 的TCP端口进程列表,有9100端口,说明监听成功触发断点调试在你需要的代码处打断点,这是基础和必须,就不多介绍了。触发PhpStorm的debug方式主要有两种GET、POST存在XDEBUG_SESSION_START 参数或者cookie里存在XDEBUG_SESSION,单用户调试 值随意。GET、POST会生成cookie XDEBUG_SESSION,有效期为1个小时(可修改php.ini配置)。很多教程需要下载Xdebug helper等浏览器插件,原理就是请求里附加cookie。php.ini文件 xdebug.remote_autostart 参数设置为On或者1,无需设置GET、POST、cookie。(不推荐,虽然不需要设置参数,设置针对环境,而不是项目,还要重启php环境)浏览器调试针对第1种,教大家用一种更简单的方式,不需要下载浏览器插件。找到PhpStorm右上角然后点小甲虫图标会打开浏览器访问你的url,并且携带GET参数XDEBUG_SESSION_START,同时生成cookie XDEBUG_SESSION,也就是说,下次(cookie有效期1小时)无需点击小甲虫,浏览器直接输入,如www.a.com或www.a.com/chat/1/即可触发调试Postman接口调试针对第1种,Postman触发方式如图可以直接添加GET、POST参数,在请求结束后,会自动附加cookie XDEBUG_SESSION,也可以手动添加cookie XDEBUG_SESSION。
2024年04月02日
422 阅读
0 评论
0 点赞
2023-12-23
php-beast加解密教程
php-beast 非常易用 效率又高的php代码保护扩展,介绍一下 windows 和linux上安装和使用的问题 ,以及如何处理常见解密问题1)Windows版本加密处理https://github.com/imaben/php-beast-binarieswindows下 可以直接在这里下载dll根据自己的php版本 还有是不是线程安全的 来选择下载对应的 放到对应的扩展目录 中在配置文件 php.ini中加入扩展配置extension=php_beast_x86_nts.dll但是秘钥是一样的 编译的话很多时候 会失败 或者出现lib版本不匹配问题老版本 我用vs编译过 dll收到版本限制比较麻烦处理官方提供的版本很稳定 直接二进制修改对应秘钥部分就可以了【1】处理aes的部分 ida分析到 变了对应的位置static uint8_t key[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,};根据文件偏移 用ue二进制编辑 找到对应位置 修改 保存即可【2】处理des部分ida逆向分析出秘钥变量的位置 和源码里面static char key[8] = { 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x0e, 0x01, 0x0e,};正好对应上 用ue二进制编辑 修改保存测试通过 其他加密方式就不处理了 [3] encrypt_file_header_sign 修改 encrypt_file_header_sign 修改ida分析到 头 信息 位置 这个数据应该是在加密的文件最前面来标示文件是否被加密了 尽量特别一点我的版本 在76760这个位置长度是8 2)Linux 版本加密处理 编译前需要修改秘钥 新版本的加密方式比较多 需要修改的地方也比较多老版本的是在key.c中修改 新版 修改是在header.c中aes_algo_lib.cdes_algo_handler.cheader.c编译步骤如下120 wget https://github.com/liexusong/php-beast/archive/master.zip121 unzip master.zip122 cd php-beast-master/124 phpize125 ./configure --with-php-config=/apps/php/bin/php-config报错 找不到 php-config找一下自己的 配置在哪里130 find / -name "php-config"确定配置文件位置131 ./configure --with-php-config=/usr/local/php/bin/php-config132 sudo make && make install133 sudo make test134 php -m发现没成功 重新试一遍135 ./configure --with-php-config=/usr/local/php/bin/php-config136 sudo make && make install后面也要加sudo root权限137 sudo make && sudo make install138 php -m140 sudo make test成功添加到php配置文件种141 php -i|grep ini142 gedit /usr/local/php/etc/php.ini添加 extension="beast.so"146 php -i|grep extension153 service php-fpm restart重启php 开始加密测试154 cd tools/161 php encode_file.php --encrypt DES --oldfile 1.php --newfile 22.php162 php 22.php163 cat 221.php 加密后生成 2.php 加密后运行测试正常 3)php-beast解密 [1]修改过的密钥的 很难解密未知密钥只能暴力破解 分析 加密的文件头部 判断是 哪一种类型加密 三种加密类型 大多数是aes des的 base64的应该没人用 分析出加密类型 对文件主体进行破解分析 -----------------------分析说明 B----------------------------- #define INT_SIZE (sizeof(int)) x64上占4个字节 php_stream_write(output_stream,encrypt_file_header_sign, encrypt_file_header_length); php_stream_write(output_stream, (const char *)&dumplen, INT_SIZE); 长度 php_stream_write(output_stream, (const char *)&expireval, INT_SIZE); 过期时间 php_stream_write(output_stream, (const char *)&dumptype, INT_SIZE); 加密类型 des=1 BEAST_ENCRYPT_TYPE_DES = 1, dumptype默认des 值为1 BEAST_ENCRYPT_TYPE_AES, BEAST_ENCRYPT_TYPE_BASE64, 使用beast_encode_file()函数加密文件,函数原型如下:beast_encode_file(string $input_file, string $output_file, int expire_timestamp, int encrypt_type)。$input_file: 要加密的文件$output_file: 输出的加密文件路径$expire_timestamp: 文件过期时间戳$encrypt_type: 加密使用的算法(支持:BEAST_ENCRYPT_TYPE_DES、BEAST_ENCRYPT_TYPE_AES) -----------------------分析说明 E----------------------------- 【2】默认密钥加密的 ,未修改过的最多 大多数保护代码的三方为了安装容易 都会使用默认的密钥加密方式,否则一个服务器部署多个版本php-beast 会增加难度 易用性降低 默认密钥 如下 自己解密主体就好了 des_key = "11" aes_key=2b,懒人也可以 用免费的工具 解密默认的php-beast加密的文件 点击 传送门phpbeast-beast解密decode-在线工具
2023年12月23日
546 阅读
0 评论
0 点赞
2023-07-07
模拟浏览器抓取网页信息
在我们日常做采集数据的时候,经常会遇到采集源使用前后端分离的,查看源代码什么数据都没有,接口也是加密的,无法采集到数据,那这个时候怎么办呢?这个时候我们可以使用 WebDriver去采集内容,它可以帮你获得页面加载完成并获取完整的渲染后的页面内容,包括动态生成的内容。这意味着您可以采集到前后端分离网站上通过 JavaScript 或 AJAX 加载的数据。接下来让我自己动手写一个试试看。Part1centos 安装 google-chrome和chromedrive1下载 Chrome 浏览器的安装包:wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm2安装 Chrome 浏览器:sudo yum localinstall google-chrome-stable_current_x86_64.rpm3验证 Chrome 是否成功安装:google-chrome --version4下载对应版本的 ChromeDriver,并解压到指定目录(/usr/local/bin):wget https://chromedriver.storage.googleapis.com/114.0.5735.90/chromedriver_linux64.zipunzip chromedriver_linux64.zip -d /usr/local/bin/5启动 ChromeDriver 服务:LANGUAGE=ZH-CN.UTF-8 /usr/local/bin/chromedriver --port=95156成功运行后(不要关闭),您应该会看到类似以下输出:Starting ChromeDriver {version} on port 9515...Only local connections are allowed.Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.ChromeDriver was started successfully.Part2PHP代码实现当做完前面两步准备工作,安装好了浏览器(Google Chrome)与浏览器驱动程序(Chromdriver)之后,总算可以进入主题,安装与使用 Php-webdriver 了。我们先再试试看bilibili这个网站,比如:下图的大世界扭蛋机这个标题(图1),我们查看网页源码是没有对应的视频标题的(图2)。(图1)(图2)现在让我们试试看使用webdriver扩展去抓取试试看。使用composer安装Php-webdrivercomposer require php-webdriver/webdriver写php代码<?phprequire_once('vendor/autoload.php');use Facebook\WebDriver\Remote\DesiredCapabilities;use Facebook\WebDriver\Remote\RemoteWebDriver;use Facebook\WebDriver\Chrome\ChromeOptions;$options = new ChromeOptions();$options->addArguments(['--no-sandbox','--headless']);$capabilities = DesiredCapabilities::chrome();$capabilities->setCapability(ChromeOptions::CAPABILITY, $options);$host = 'http://localhost:9555';$driver = RemoteWebDriver::create($host, $capabilities);$url = 'https://www.bilibili.com/movie/index/?from_spmid=666.7.index.1#st=2&style_id=10104&area=-1&release_date=-1&season_status=-1&order=2&sort=0&page=1';// 访问网站$driver->get($url);// 获取页面源代码$pageSource = $driver->getPageSource();echo $pageSource;// 关闭 WebDriver 会话$driver->quit();运行结果成功获取源代码。Part3介绍一下xpathXPath 是一种用于在 XML 或 HTML 文档中定位元素的语言。在 PHP WebDriver 中,XPath 是一个重要的工具,可以帮助我们定位和操作页面上的元素。XPath 表达式由节点选择器和路径表达式组成。以下是一些常用的 XPath 语法和示例:选择元素://tagname:选择所有具有指定标签名称的元素。//tagname[@attribute='value']:选择具有指定属性值的元素。//tagname[text()='text']:选择具有指定文本内容的元素。选择父子关系:/parent/child:选择父节点下的直接子节点。/parent//descendant:选择父节点下所有后代节点。选择兄弟关系:/preceding-sibling::sibling:选择当前节点之前的同级节点。/following-sibling::sibling:选择当前节点之后的同级节点。使用逻辑运算符:and、or、not():使用逻辑运算符进行条件组合。使用通配符:*:匹配任意元素。@*:匹配任意属性。以下是一个使用 XPath 定位元素并执行操作的示例代码:<?php$driver = RemoteWebDriver::create($host, $capabilities);// 打开网页$driver->get('https://www.example.com/');// 使用 XPath 定位元素并执行操作$element = $driver->findElement(WebDriverBy::xpath("//input[@name='username']"));$element->sendKeys('admin');// 关闭 WebDriver 连接$driver->quit();上述代码中,我们使用 $driver->findElement() 方法和 WebDriverBy::xpath() 来通过 XPath 表达式定位页面上的元素。在示例中,我们使用了 //input[@name='username'] 的 XPath 表达式来选择具有 name 属性值为 'username' 的 <input> 元素。然后,我们使用 $element->sendKeys() 方法向选定的元素发送键盘输入。请注意,XPath 是一种强大而灵活的定位工具,可以根据不同的需求编写不同的表达式。您可以根据页面结构和要定位的元素特征来编写适当的 XPath 表达式。
2023年07月07日
349 阅读
0 评论
0 点赞
2023-03-09
ASCII对应码表-键值
ASCII对应码表-键值(完整版) Bin(二进制)Oct(八进制)Dec(十进制)Hex(十六进制)缩写/字符解释0000 00000000x00NUL(null)空字符0000 00010110x01SOH(start of headline)标题开始0000 00100220x02STX (start of text)正文开始0000 00110330x03ETX (end of text)正文结束0000 01000440x04EOT (end of transmission)传输结束0000 01010550x05ENQ (enquiry)请求0000 01100660x06ACK (acknowledge)收到通知0000 01110770x07BEL (bell)响铃0000 100001080x08BS (backspace)退格0000 100101190x09HT (horizontal tab)水平制表符0000 1010012100x0ALF (NL line feed, new line)换行键0000 1011013110x0BVT (vertical tab)垂直制表符0000 1100014120x0CFF (NP form feed, new page)换页键0000 1101015130x0DCR (carriage return)回车键0000 1110016140x0ESO (shift out)不用切换0000 1111017150x0FSI (shift in)启用切换0001 0000020160x10DLE (data link escape)数据链路转义0001 0001021170x11DC1 (device control 1)设备控制10001 0010022180x12DC2 (device control 2)设备控制20001 0011023190x13DC3 (device control 3)设备控制30001 0100024200x14DC4 (device control 4)设备控制40001 0101025210x15NAK (negative acknowledge)拒绝接收0001 0110026220x16SYN (synchronous idle)同步空闲0001 0111027230x17ETB (end of trans. block)结束传输块0001 1000030240x18CAN (cancel)取消0001 1001031250x19EM (end of medium)媒介结束0001 1010032260x1ASUB (substitute)代替0001 1011033270x1BESC (escape)换码(溢出)0001 1100034280x1CFS (file separator)文件分隔符0001 1101035290x1DGS (group separator)分组符0001 1110036300x1ERS (record separator)记录分隔符0001 1111037310x1FUS (unit separator)单元分隔符0010 0000040320x20(space)空格0010 0001041330x21!叹号0010 0010042340x22"双引号0010 0011043350x23#井号0010 0100044360x24$美元符0010 0101045370x25%百分号0010 0110046380x26&和号0010 0111047390x27'闭单引号0010 1000050400x28(开括号0010 1001051410x29)闭括号0010 1010052420x2A*星号0010 1011053430x2B+加号0010 1100054440x2C,逗号0010 1101055450x2D-减号/破折号0010 1110056460x2E.句号0010 1111057470x2F/斜杠0011 0000060480x300字符00011 0001061490x311字符10011 0010062500x322字符20011 0011063510x333字符30011 0100064520x344字符40011 0101065530x355字符50011 0110066540x366字符60011 0111067550x377字符70011 1000070560x388字符80011 1001071570x399字符90011 1010072580x3A:冒号0011 1011073590x3B;分号0011 1100074600x3C<小于0011 1101075610x3D=等号0011 1110076620x3E>大于0011 1111077630x3F?问号0100 00000100640x40@电子邮件符号0100 00010101650x41A大写字母A0100 00100102660x42B大写字母B0100 00110103670x43C大写字母C0100 01000104680x44D大写字母D0100 01010105690x45E大写字母E0100 01100106700x46F大写字母F0100 01110107710x47G大写字母G0100 10000110720x48H大写字母H0100 10010111730x49I大写字母I010010100112740x4AJ大写字母J0100 10110113750x4BK大写字母K0100 11000114760x4CL大写字母L0100 11010115770x4DM大写字母M0100 11100116780x4EN大写字母N0100 11110117790x4FO大写字母O0101 00000120800x50P大写字母P0101 00010121810x51Q大写字母Q0101 00100122820x52R大写字母R0101 00110123830x53S大写字母S0101 01000124840x54T大写字母T0101 01010125850x55U大写字母U0101 01100126860x56V大写字母V0101 01110127870x57W大写字母W0101 10000130880x58X大写字母X0101 10010131890x59Y大写字母Y0101 10100132900x5AZ大写字母Z0101 10110133910x5B[开方括号0101 11000134920x5C\反斜杠0101 11010135930x5D]闭方括号0101 11100136940x5E^脱字符0101 11110137950x5F_下划线0110 00000140960x60`开单引号0110 00010141970x61a小写字母a0110 00100142980x62b小写字母b0110 00110143990x63c小写字母c0110 010001441000x64d小写字母d0110 010101451010x65e小写字母e0110 011001461020x66f小写字母f0110 011101471030x67g小写字母g0110 100001501040x68h小写字母h0110 100101511050x69i小写字母i0110 101001521060x6Aj小写字母j0110 101101531070x6Bk小写字母k0110 110001541080x6Cl小写字母l0110 110101551090x6Dm小写字母m0110 111001561100x6En小写字母n0110 111101571110x6Fo小写字母o0111 000001601120x70p小写字母p0111 000101611130x71q小写字母q0111 001001621140x72r小写字母r0111 001101631150x73s小写字母s0111 010001641160x74t小写字母t0111 010101651170x75u小写字母u0111 011001661180x76v小写字母v0111 011101671190x77w小写字母w0111 100001701200x78x小写字母x0111 100101711210x79y小写字母y0111 101001721220x7Az小写字母z0111 101101731230x7B{开花括号0111 110001741240x7C|垂线0111 110101751250x7D}闭花括号0111 111001761260x7E~波浪号0111 111101771270x7FDEL (delete)删除
2023年03月09日
438 阅读
0 评论
0 点赞
2023-03-09
可以自定义密钥的PHP源码加密模块
PHP-Beast是一个PHP源码加密的模块,其使用DES算法加密,用户可以自定义加密的key来加密源代码。PHP-Beast是一个PHP源码加密的模块,其使用DES算法加密,用户可以自定义加密的key来加密源代码。1、PHP-Beast的安装1234567$ wget https://github.com/liexusong/php-beast/archive/master.zip$ unzip master.zip$ cd php-beast-master$ phpize$ ./configure$ make$ sudo make install注意:在编译扩展之前先修改加密的key,如下图。编译好之后修改php.ini配置文件,加入配置项:extension=beast.so,重启php-fpm。安装完成后可以在phpinfo中看到如下结果:2、安装好PHP-Beast模块后,可以使用工具包中的encrypt_project.php工具来加密你的项目,你只需要输入项目的路径和输出的项目路径即可(这个工具只会加密PHP文件,其他文件只会简单的复制),如:加密完成后使用文本编辑器看编译后的文件可以发现已经变为乱码,如:3. 当然你可能不希望加密项目的所有PHP源文件,所以你可以使用PHP-Beast模块提供的beast_encode_file()函数来加密其中的某个文件,使用方法:1beast_encode_file($input_file, $output_file)$input_file是要加密的文件,$output_file是要输出加密后的文件。4. 把源码加密后就可以放到服务器上运行了。另外PHP-Beast模块提供缓存功能,我们可以在php.ini文件中修改缓存的大小来控制PHP-Beast模块可以使用的缓存。如:12[beast]beast.cache_size = 20000我们可以使用beast_cache_status()函数来查看缓存的状态。
2023年03月09日
522 阅读
0 评论
0 点赞
2022-11-09
thinkphp 404锛屾偍璇锋眰鐨勬枃浠朵笉瀛樺湪!
thinkphp6 当环境的pathinfo没启动的时候会报 404锛屾偍璇锋眰鐨勬枃浠朵笉瀛樺湪! 错误报错环境 系统windows10 宝塔面板 nginx php7.4修改nginx配置文件 php 74.conflocation ~ \.php(.*)$ { try_files $uri =404; fastcgi_pass 127.0.0.1:20074; fastcgi_index index.php; include fastcgi.conf; include pathinfo.conf; }重启php和nginx服务就可以解决
2022年11月09日
2,156 阅读
0 评论
0 点赞
2022-06-14
nginx 设置详细日志记录
nginx 日志记录详细请求信息nginx.confworker_processes auto; worker_rlimit_nofile 51200; events { worker_connections 51200; multi_accept on; } http { include mime.types; #include luawaf.conf; include proxy.conf; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" $request_time'; log_format log_json '{ "timestamp": "$time_local", ' '"remote_addr": "$remote_addr", ' '"referer": "$http_referer", ' '"request": "$request", ' '"status": $status, ' '"bytes": $body_bytes_sent, ' '"agent": "$http_user_agent", ' '"x_forwarded": "$http_x_forwarded_for", ' '"up_addr": "$upstream_addr",' '"up_host": "$upstream_http_host",' '"up_resp_time": "$upstream_response_time",' '"request_time": "$request_time"' ' }'; log_format main_all '[$time_local] client=$remote_addr ' 'request="$request" request_length=$request_length ' 'http_referer="$http_referer" ' 'bytes_sent=$bytes_sent ' 'body_bytes_sent=$body_bytes_sent ' 'user_agent="$http_user_agent" ' 'upstream_addr=$upstream_addr ' 'upstream_status=$upstream_status ' 'cookie="$http_cookie" ' 'request_body="$request_body" ' 'document_root="$document_root" ' 'fastcgi_script_name="$fastcgi_script_name" ' 'request_filename="$request_filename" ' 'request_time=$request_time ' 'upstream_response_time=$upstream_response_time ' 'upstream_connect_time=$upstream_connect_time ' 'upstream_header_time=$upstream_header_time '; log_format main_json escape=json '{ ' '"time_local": "$time_local", ' '"remote_addr": "$remote_addr", ' '"request": "$request", ' '"request_length": "$request_length", ' '"http_referer": "$http_referer", ' '"bytes_sent": "$bytes_sent", ' '"body_bytes_sent": "$body_bytes_sent", ' '"http_user_agent": "$http_user_agent", ' '"upstream_addr": "$upstream_addr", ' '"upstream_status": "$upstream_status", ' '"http_cookie": "$http_cookie", ' '"request_body": "$request_body", ' '"document_root": "$document_root", ' '"fastcgi_script_name": "$fastcgi_script_name", ' '"request_filename": "$request_filename", ' '"request_time": "$request_time", ' '"upstream_response_time": "$upstream_response_time", ' '"upstream_connect_time": "$upstream_connect_time", ' '"upstream_header_time": "$upstream_header_time"' ' },'; server_names_hash_bucket_size 512; client_header_buffer_size 32k; large_client_header_buffers 4 32k; client_max_body_size 50m; sendfile on; tcp_nopush on; keepalive_timeout 60; tcp_nodelay on; fastcgi_connect_timeout 300; fastcgi_send_timeout 300; fastcgi_read_timeout 300; fastcgi_buffer_size 64k; fastcgi_buffers 4 64k; fastcgi_busy_buffers_size 128k; fastcgi_temp_file_write_size 256k; fastcgi_intercept_errors on; gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 2; gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml; gzip_vary on; gzip_proxied expired no-cache no-store private auth; gzip_disable "MSIE [1-6]\."; limit_conn_zone $binary_remote_addr zone=perip:10m; limit_conn_zone $server_name zone=perserver:10m; server_tokens off; access_log off; server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } location /nginx_status { allow 127.0.0.1; deny all; stub_status on; access_log off; } } include vhost/*.conf; #加载vhost目录下的虚拟主机配置文件 }参数说明:$args #请求中的参数值$query_string #同 $args$arg_NAME #GET请求中NAME的值$is_args #如果请求中有参数,值为"?",否则为空字符串$uri #请求中的当前URI(不带请求参数,参数位于$args),可以不同于浏览器传递的$request_uri的值,它可以通过内部重定向,或者使用index指令进行修改,$uri不包含主机名,如"/foo/bar.html"。$document_uri #同 $uri$document_root #当前请求的文档根目录或别名$host #优先级:HTTP请求行的主机名>"HOST"请求头字段>符合请求的服务器名.请求中的主机头字段,如果请求中的主机头不可用,则为服务器处理请求的服务器名称$hostname #主机名$https #如果开启了SSL安全模式,值为"on",否则为空字符串。$binary_remote_addr #客户端地址的二进制形式,固定长度为4个字节$body_bytes_sent #传输给客户端的字节数,响应头不计算在内;这个变量和Apache的mod_log_config模块中的"%B"参数保持兼容$bytes_sent #传输给客户端的字节数$connection #TCP连接的序列号$connection_requests #TCP连接当前的请求数量$content_length #"Content-Length" 请求头字段$content_type #"Content-Type" 请求头字段$cookie_name #cookie名称$limit_rate #用于设置响应的速度限制$msec #当前的Unix时间戳$nginx_version #nginx版本$pid #工作进程的PID$pipe #如果请求来自管道通信,值为"p",否则为"."$proxy_protocol_addr #获取代理访问服务器的客户端地址,如果是直接访问,该值为空字符串$realpath_root #当前请求的文档根目录或别名的真实路径,会将所有符号连接转换为真实路径$remote_addr #客户端地址$remote_port #客户端端口$remote_user #用于HTTP基础认证服务的用户名$request #代表客户端的请求地址$request_body #客户端的请求主体:此变量可在location中使用,将请求主体通过proxy_pass,fastcgi_pass,uwsgi_pass和scgi_pass传递给下一级的代理服务器$request_body_file #将客户端请求主体保存在临时文件中。文件处理结束后,此文件需删除。如果需要之一开启此功能,需要设置client_body_in_file_only。如果将次文件传 递给后端的代理服务器,需要禁用request body,即设置proxy_pass_request_body off,fastcgi_pass_request_body off,uwsgi_pass_request_body off,or scgi_pass_request_body off$request_completion #如果请求成功,值为"OK",如果请求未完成或者请求不是一个范围请求的最后一部分,则为空$request_filename #当前连接请求的文件路径,由root或alias指令与URI请求生成$request_length #请求的长度 (包括请求的地址,http请求头和请求主体)$request_method #HTTP请求方法,通常为"GET"或"POST"$request_time #处理客户端请求使用的时间,单位为秒,精度毫秒; 从读入客户端的第一个字节开始,直到把最后一个字符发送给客户端后进行日志写入为止。$request_uri #这个变量等于包含一些客户端请求参数的原始URI,它无法修改,请查看$uri更改或重写URI,不包含主机名,例如:"/cnphp/test.php?arg=freemouse"$scheme #请求使用的Web协议,"http" 或 "https"$server_addr #服务器端地址,需要注意的是:为了避免访问linux系统内核,应将ip地址提前设置在配置文件中$server_name #服务器名$server_port #服务器端口$server_protocol #服务器的HTTP版本,通常为 "HTTP/1.0" 或 "HTTP/1.1"$status #HTTP响应代码$time_iso8601 #服务器时间的ISO 8610格式$time_local #服务器时间(LOG Format 格式)$cookie_NAME #客户端请求Header头中的cookie变量,前缀"$cookie_"加上cookie名称的变量,该变量的值即为cookie名称的值$http_NAME #匹配任意请求头字段;变量名中的后半部分NAME可以替换成任意请求头字段,如在配置文件中需要获取http请求头:"Accept-Language",$http_accept_language即可$http_cookie$http_host #请求地址,即浏览器中你输入的地址(IP或域名)$http_referer #url跳转来源,用来记录从那个页面链接访问过来的$http_user_agent #用户终端浏览器等信息$http_x_forwarded_for$sent_http_NAME #可以设置任意http响应头字段;变量名中的后半部分NAME可以替换成任意响应头字段,如需要设置响应头Content-length,$sent_http_content_length即可$sent_http_cache_control$sent_http_connection$sent_http_content_type$sent_http_keep_alive$sent_http_last_modified$sent_http_location$sent_http_transfer_encoding
2022年06月14日
1,063 阅读
0 评论
0 点赞
2022-02-27
阿里云ECS数据盘扩容操作
服务器系统是Centos7,环境是宝塔面板,数据都存在宝塔默认目录www;数据盘在使用一段时间后满了,然后在阿里云控制台云盘管理中直接扩容满了的那个盘。扩容之后登录服务器查看数据盘依旧是原来大小,解决办法:
2022年02月27日
1,315 阅读
0 评论
0 点赞
2022-02-21
使用ffmpeg视频切片并加密
想达到的目的:将一个mp4视频文件切割为多个ts片段,并在切割过程中对每一个片段使用 AES-128 加密,最后生成一个m3u8的视频索引文件;电脑环境 Fedora,已经安装了最新的ffmpeg;如果要加密,首先准备好一下两个东西: 加密用的 keyopenssl rand 16 > enc.key ( 生成一个enc.key文件 ) 另一个是 ivopenssl rand -hex 16 ( 生成一段字符串,记下来) 新建一个文件 enc.keyinfo 内容格式如下:Key URI # enc.key的路径,使用http形式 Path to key file # enc.key文件 IV # 上面生成的iv 几个例子:http://localhost/video/enc.keyenc.key 48c674428c1e719751565ad00fe24243 最后重点来了 ffmpeg -y \-i test.mp4 \-hls_time 12 \ # 将test.mp4分割成每个小段多少秒-hls_key_info_file enc.keyinfo \-hls_playlist_type vod \ # vod 是点播,表示PlayList不会变-hls_segment_filename "file%d.ts" \ # 每个小段的文件名 playlist.m3u8 # 生成的m3u8文件 生成的m3u8文件可以通过大部分播放器直接播放
2022年02月21日
2,337 阅读
0 评论
0 点赞
2022-01-28
2022最新WPS政府/教育版合集
【教育考试专版】下载地址:http://ncre.neea.edu.cn/html1/report/1507/861-1.htm 【温州大学】http://itc.wzu.edu.cn/info/1032/3661.htm 【云南师范大学】https://it.ynnu.edu.cn/info/1146/2421.htm 【西南民族大学】http://ms.swun.edu.cn/download/wps.html 【大庆市人民政府】http://www.daqing.gov.cn/xiangguanxiazai/14436.html 【富蕴县党建网】http://www.xjfydj.com/P/C/66148.htm 【晋城市党政机关】https://wps.jcgov.gov.cn/downloadOffice 合集下载地址:https://www.123pan.com/s/VzVA-murbd 提取码:9neD
2022年01月28日
3,282 阅读
0 评论
0 点赞
idea激活
1. 在Licence Server 激活:(以下总有可以的吧))http://idea.imsxm.com/ (亲测直至目前均可用)http://www.0-php.com:1017http://idea.singee77.com/ http://idea.lanyus.com/ http://idea.qinxi1992.cn/2. 浏览器打开 http://idea.lanyus.com/ , 点击页面中的“获得注册码”,然后在注册时切换至Activation Code选项,输入获得的注册码一长串字符串,就可以注册成功!(推荐方法)本教程分享两种破解方法,小伙伴们二选一即可:1. 第一种:👉 [(补丁)无限重置 30天试用期]PS: 第一种仅适用于 PhpStorm 2021.2.2 (包含 2021.2.2 版本) 以下版本,可下载 PhpStorm 历史版本来使用。2. 第二种:👉 (补丁+激活码)可激活 PhpStorm 到 2099 年,即本文教程所写的,具体步骤跟着下面的图文教程一步一步来就行,一分钟即可搞定,过程也非常简单。无图无真相,下面截图是成功激活到 2099 的:注意本教程适用于 PhpStorm IntelliJ IDEA 2021.3 以下所有版本,请放心食用~本教程适用于 JetBrains 全系列产品,包括 IDEA、Pycharm、WebStorm、Phpstorm、Datagrip、RubyMine、CLion、AppCode 等。本教程适用 Windows/Mac/Linux 系统,文中以 Windows 系统为例做讲解,其他系统按照教程顺序即可。教程开始1. 运行 PhpStorm 2021.3 运行, 中间会先弹出一个注册框,我们勾选 Evaluate for free, 点击 Evaluate, 先试用30天:注意,如果没有弹出上面的界面,可先执行 reset_script 文件夹中的重置试用期脚本,脚本地址下文可获取,先进到 PhpStorm 里面打开该文件夹后,有对应系统的执行脚本,执行即可:windows系统:reset_jetbrains_eval_windows.vbs linux/mac系统:reset_jetbrains_eval_mac_linux.sh最新的 PhpStorm 2021.3 版本界面发生了变化,取消了直接试用 30 天的按钮,需要我们先注册一个 JetBrains,如下:PS: 部分小伙伴反映点击注册 PhpStorm 按钮后,网页打不开,无法访问,原因是因为他在 hosts 文件中添加了东西,删除还原回去就能正常访问了。注册账号并登录后,就可以试用2. 清空 IDEA 以前使用过的激活方式【非常重要】如果你之前安装过 PhpStorm, 那么修改过的 hosts 文件要还原回去、引用过的补丁要移除掉等, 不然可能会有各种奇奇怪怪的问题。3. 引用破解补丁包注册好 JetBrains 账号后,登录打开 PhpStorm, 先随便新建个项目或者打开个旧项目:然后依次点击菜单 Helo -> Edit Custom VM Options 来通过 PhpStorm 修改 idea.vmoptions 文件, 在末尾引用破解补丁,补丁下文可获取 :# 我这边将补丁放置在了D盘根目录下,小伙伴们可以根据自己实际的位置进行修改 -javaagent:D:\\FineAgent.jar4. 重启IDEA !重启PhpStorm!【非常重要,必须】引用补丁成功后,需要重启 PhpStorm,才能加载补丁。5. 填入激活码重启 PhpStorm 后,开始输入激活码,先将 ActivationCode.txt 文件中的激活码复制出来;输入激活码:至此,大功告成,可以看到我们已经成功激活到 2099 年了~
2022年01月05日
696 阅读
0 评论
0 点赞
2021-06-17
nginx二级目录反向代理
location /htmls/ { proxy_pass http://domain/htmls/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }
2021年06月17日
930 阅读
0 评论
0 点赞
2021-06-08
搭建PSI
进入主题,PSI的安装过程:1-源码下载地址:http://git.oschina.net/crm8000/PSI2-下载以后上传到网站的根目录,进行解压,解压以后的文件目录如下# cd /web/test.psi.com/wwwroot# ls -l3-创建数据库 创建库名为psi,字符集为utf8的库,授权psier用户来登录,密码为abcd.1234mysql> create database psi character set utf8;mysql> grant all on psi.* to 'psier'@'localhost' identified by 'abcd.1234';4-导入初始化数据库数据# cd doc/99\ SQL/# mysql -upsier -h127.0.0.1 -pabcd.1234 psi < 01CreateTables.sql --导入表结构# mysql -upsier -h127.0.0.1 -pabcd.1234 psi < 02InsertInitData.sql --导入初始化数据# mysql -upsier -h127.0.0.1 -pabcd.1234 psi < 99psi_demo_data.sql --导入测试数据(这一步可选,我没有导入)5-修改配置文件的数据库连接# vim web/Application/Common/Conf/config.php 修改function PSI_getUser和PSI_getPassword 中的return 值如下图修改以上两个地方,其他不变,保存退出。6-配置nginx.confserver { listen 80; server_name test.psi.com; root /web/test.psi.com/wwwroot; index index.html index.htm default.htm index.php; location / { if (!-e $request_filename){ rewrite ^/web/(.*)$ /web/index.php/$1 last; #--关键的配置,支持ThinkPHP的rewrite支持 } } location ~ .*\.php { #--经测试,必须以去除?$结尾,去掉$是为了不匹配行末,即可以匹配.php/,以实现pathinfo fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi.conf; include pathinfo.conf; #--关键的配置,支持ThinkPHP的pathinfo支持 } access_log logs/test.psi.com_access.log main; error_log logs/test.psi.com_error.log; }# cat /usr/local/nginx/conf/pathinfo.confset $real_script_name $fastcgi_script_name;if ($fastcgi_script_name ~ "(.+?\.php)(/.*)") {set $real_script_name $1;set $path_info $2; }fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;fastcgi_param SCRIPT_NAME $real_script_name;fastcgi_param PATH_INFO $path_info;7-重启nginx# /usr/local/nginx/sbin/nginx -s reload8-浏览器测试访问
2021年06月08日
1,189 阅读
0 评论
0 点赞
2021-04-26
深入理解控制反转(IoC)和依赖注入(DI)
容器,字面上理解就是装东西的东西。常见的变量、对象属性等都可以算是容器。一个容器能够装什么,全部取决于你对该容器的定义。当然,有这样一种容器,它存放的不是文本、数值,而是对象、对象的描述(类、接口)或者是提供对象的回调,通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 “解耦” 、“依赖注入(DI)”。本文就从这里开始。IoC 容器, laravel 的核心Laravel 的核心就是一个 IoC 容器,根据文档,称其为“服务容器”,顾名思义,该容器提供了整个框架中需要的一系列服务。作为初学者,很多人会在这一个概念上犯难,因此,我打算从一些基础的内容开始讲解,通过理解面向对象开发中依赖的产生和解决方法,来逐渐揭开“依赖注入”的面纱,逐渐理解这一神奇的设计理念。本文一大半内容都是通过举例来让读者去理解什么是 IoC(控制反转) 和 DI(依赖注入),通过理解这些概念,来更加深入。更多关于 laravel 服务容器的用法建议阅读文档即可。IoC 容器诞生的故事讲解 IoC 容器有很多的文章,我之前也写过。但现在我打算利用当下的灵感重新来过,那么开始吧。超人和超能力,依赖的产生!面向对象编程,有以下几样东西无时不刻的接触:接口、类还有对象。这其中,接口是类的原型,一个类必须要遵守其实现的接口;对象则是一个类实例化后的产物,我们称其为一个实例。当然这样说肯定不利于理解,我们就实际的写点中看不中用的代码辅助学习。怪物横行的世界,总归需要点超级人物来摆平。我们把一个“超人”作为一个类,class Superman {}我们可以想象,一个超人诞生的时候肯定拥有至少一个超能力,这个超能力也可以抽象为一个对象,为这个对象定义一个描述他的类吧。一个超能力肯定有多种属性、(操作)方法,这个尽情的想象,但是目前我们先大致定义一个只有属性的“超能力”,至于能干啥,我们以后再丰富:class Power { /** * 能力值 */ protected $ability; /** * 能力范围或距离 */ protected $range; public function __construct($ability, $range) { $this->ability = $ability; $this->range = $range; } }这时候我们回过头,修改一下之前的“超人”类,让一个“超人”创建的时候被赋予一个超能力:class Superman{ protected $power; public function __construct() { $this->power = new Power(999, 100); } }这样的话,当我们创建一个“超人”实例的时候,同时也创建了一个“超能力”的实例,但是,我们看到了一点,“超人”和“超能力”之间不可避免的产生了一个依赖。所谓“依赖”,就是 “我若依赖你,我就不能离开你”。在一个贯彻面向对象编程的项目中,这样的依赖随处可见。少量的依赖并不会有太过直观的影响,我们随着这个例子逐渐铺开,让大家慢慢意识到,当依赖达到一个量级时,是怎样一番噩梦般的体验。当然,我也会自然而然的讲述如何解决问题。一堆乱麻 —— 可怕的依赖之前的例子中,超能力类实例化后是一个具体的超能力,但是我们知道,超人的超能力是多元化的,每种超能力的方法、属性都有不小的差异,没法通过一种类描述完全。我们现在进行修改,我们假设超人可以有以下多种超能力:飞行,属性有:飞行速度、持续飞行时间蛮力,属性有:力量值能量弹,属性有:伤害值、射击距离、同时射击个数我们创建了如下类:class Flight{ protected $speed; protected $holdtime; public function __construct($speed, $holdtime) {} }class Force{ protected $force; public function __construct($force) {} }class Shot{ protected $atk; protected $range; protected $limit; public function __construct($atk, $range, $limit) {} }*为了省事儿我没有详细写出 __construct() 这个构造函数的全部,只写了需要传递的参数。好了,这下我们的超人有点“忙”了。在超人初始化的时候,我们会根据需要来实例化其拥有的超能力吗,大致如下:class Superman{ protected $power; public function __construct() { $this->power = new Fight(9, 100); // $this->power = new Force(45); // $this->power = new Shot(99, 50, 2); /* $this->power = array( new Force(45), new Shot(99, 50, 2) ); */ } }我们需要自己手动的在构造函数内(或者其他方法里)实例化一系列需要的类,这样并不好。可以想象,假如需求变更(不同的怪物横行地球),需要更多的有针对性的 新的 超能力,或者需要 变更 超能力的方法,我们必须 重新改造 超人。换句话说就是,改变超能力的同时,我还得重新制造个超人。效率太低了!新超人还没创造完成世界早已被毁灭。这时,灵机一动的人想到:为什么不可以这样呢?超人的能力可以被随时更换,只需要添加或者更新一个芯片或者其他装置啥的(想到钢铁侠没)。这样的话就不要整个重新来过了。对,就是这样的。我们不应该手动在 “超人” 类中固化了他的 “超能力” 初始化的行为,而转由外部负责,由外部创造超能力模组、装置或者芯片等(我们后面统一称为 “模组”),植入超人体内的某一个接口,这个接口是一个既定的,只要这个 “模组” 满足这个接口的装置都可以被超人所利用,可以提升、增加超人的某一种能力。这种由外部负责其依赖需求的行为,我们可以称其为 “控制反转(IoC)”。工厂模式,依赖转移!当然,实现控制反转的方法有几种。在这之前,不如我们先了解一些好玩的东西。我们可以想到,组件、工具(或者超人的模组),是一种可被生产的玩意儿,生产的地方当然是 “工厂(Factory)”,于是有人就提出了这样一种模式: 工厂模式。工厂模式,顾名思义,就是一个类所依赖的外部事物的实例,都可以被一个或多个 “工厂” 创建的这样一种开发模式,就是 “工厂模式”。我们为了给超人制造超能力模组,我们创建了一个工厂,它可以制造各种各样的模组,且仅需要通过一个方法:class SuperModuleFactory{ public function makeModule($moduleName, $options) { switch ($moduleName) { case 'Fight': return new Fight($options[0], $options[1]); case 'Force': return new Force($options[0]); case 'Shot': return new Shot($options[0], $options[1], $options[2]); } } }这时候,超人 创建之初就可以使用这个工厂!class Superman{ protected $power; public function __construct() { // 初始化工厂 $factory = new SuperModuleFactory; // 通过工厂提供的方法制造需要的模块 $this->power = $factory->makeModule('Fight', [9, 100]); // $this->power = $factory->makeModule('Force', [45]); // $this->power = $factory->makeModule('Shot', [99, 50, 2]); /* $this->power = array( $factory->makeModule('Force', [45]), $factory->makeModule('Shot', [99, 50, 2]) ); */ } }可以看得出,我们不再需要在超人初始化之初,去初始化许多第三方类,只需初始化一个工厂类,即可满足需求。但这样似乎和以前区别不大,只是没有那么多 new 关键字。其实我们稍微改造一下这个类,你就明白,工厂类的真正意义和价值了。class Superman{ protected $power; public function __construct(array $modules) { // 初始化工厂 $factory = new SuperModuleFactory; // 通过工厂提供的方法制造需要的模块 foreach ($modules as $moduleName => $moduleOptions) { $this->power[] = $factory->makeModule($moduleName, $moduleOptions); } } }// 创建超人$superman = new Superman([ 'Fight' => [9, 100], 'Shot' => [99, 50, 2] ]);现在修改的结果令人满意。现在,“超人” 的创建不再依赖任何一个 “超能力” 的类,我们如若修改了或者增加了新的超能力,只需要针对修改 SuperModuleFactory 即可。扩充超能力的同时不再需要重新编辑超人的类文件,使得我们变得很轻松。但是,这才刚刚开始。再进一步!IoC 容器的重要组成 —— 依赖注入!由 “超人” 对 “超能力” 的依赖变成 “超人” 对 “超能力模组工厂” 的依赖后,对付小怪兽们变得更加得心应手。但这也正如你所看到的,依赖并未解除,只是由原来对多个外部的依赖变成了对一个 “工厂” 的依赖。假如工厂出了点麻烦,问题变得就很棘手。其实大多数情况下,工厂模式已经足够了。工厂模式的缺点就是:接口未知(即没有一个很好的契约模型,关于这个我马上会有解释)、产生对象类型单一。总之就是,还是不够灵活。虽然如此,工厂模式依旧十分优秀,并且适用于绝大多数情况。不过我们为了讲解后面的 依赖注入 ,这里就先夸大一下工厂模式的缺陷咯。我们知道,超人依赖的模组,我们要求有统一的接口,这样才能和超人身上的注入接口对接,最终起到提升超能力的效果。事实上,我之前说谎了,不仅仅只有一堆小怪兽,还有更多的大怪兽。嘿嘿。额,这时候似乎工厂的生产能力显得有些不足 —— 由于工厂模式下,所有的模组都已经在工厂类中安排好了,如果有新的、高级的模组加入,我们必须修改工厂类(好比增加新的生产线):class SuperModuleFactory{ public function makeModule($moduleName, $options) { switch ($moduleName) { case 'Fight': return new Fight($options[0], $options[1]); case 'Force': return new Force($options[0]); case 'Shot': return new Shot($options[0], $options[1], $options[2]); // case 'more': ....... // case 'and more': ....... // case 'and more': ....... // case 'oh no! its too many!': ....... } } }看到没。。。噩梦般的感受!其实灵感就差一步!你可能会想到更为灵活的办法!对,下一步就是我们今天的主要配角 —— DI (依赖注入)由于对超能力模组的需求不断增大,我们需要集合整个世界的高智商人才,一起解决问题,不应该仅仅只有几个工厂垄断负责。不过高智商人才们都非常自负,认为自己的想法是对的,创造出的超能力模组没有统一的接口,自然而然无法被正常使用。这时我们需要提出一种契约,这样无论是谁创造出的模组,都符合这样的接口,自然就可被正常使用。interface SuperModuleInterface{ /** * 超能力激活方法 * * 任何一个超能力都得有该方法,并拥有一个参数 *@param array $target 针对目标,可以是一个或多个,自己或他人 */ public function activate(array $target); }上文中,我们定下了一个接口 (超能力模组的规范、契约),所有被创造的模组必须遵守该规范,才能被生产。其实,这就是 php 中 接口( interface ) 的用处和意义!很多人觉得,为什么 php 需要接口这种东西?难道不是 java 、 C# 之类的语言才有的吗?这么说,只要是一个正常的面向对象编程语言(虽然 php 可以面向过程),都应该具备这一特性。因为一个 对象(object) 本身是由他的模板或者原型 —— 类 (class) ,经过实例化后产生的一个具体事物,而有时候,实现统一种方法且不同功能(或特性)的时候,会存在很多的类(class),这时候就需要有一个契约,让大家编写出可以被随时替换却不会产生影响的接口。这种由编程语言本身提出的硬性规范,会增加更多优秀的特性。虽然有些绕,但通过我们接下来的实例,大家会慢慢领会接口带来的好处。这时候,那些提出更好的超能力模组的高智商人才,遵循这个接口,创建了下述(模组)类:/** * X-超能量 */class XPower implements SuperModuleInterface{ public function activate(array $target) { // 这只是个例子。。具体自行脑补 } }/** * 终极炸弹 (就这么俗) */class UltraBomb implements SuperModuleInterface{ public function activate(array $target) { // 这只是个例子。。具体自行脑补 } }同时,为了防止有些 “砖家” 自作聪明,或者一些叛徒恶意捣蛋,不遵守契约胡乱制造模组,影响超人,我们对超人初始化的方法进行改造:class Superman{ protected $module; public function __construct(SuperModuleInterface $module) { $this->module = $module } }改造完毕!现在,当我们初始化 “超人” 类的时候,提供的模组实例必须是一个 SuperModuleInterface 接口的实现。否则就会提示错误。正是由于超人的创造变得容易,一个超人也就不需要太多的超能力,我们可以创造多个超人,并分别注入需要的超能力模组即可。这样的话,虽然一个超人只有一个超能力,但超人更容易变多,我们也不怕怪兽啦!现在有人疑惑了,你要讲的 依赖注入 呢?其实,上面讲的内容,正是依赖注入。什么叫做 依赖注入?本文从开头到现在提到的一系列依赖,只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于 依赖注入(DI) 。是不是豁然开朗?事实上,就是这么简单。下面就是一个典型的依赖注入:// 超能力模组$superModule = new XPower;// 初始化一个超人,并注入一个超能力模组依赖$superMan = new Superman($superModule);关于依赖注入这个本文的主要配角,也就这么多需要讲的。理解了依赖注入,我们就可以继续深入问题。慢慢走近今天的主角……更为先进的工厂 —— IoC 容器!刚刚列了一段代码:$superModule = new XPower;$superMan = new Superman($superModule);读者应该看出来了,手动的创建了一个超能力模组、手动的创建超人并注入了刚刚创建超能力模组。呵呵,手动。现代社会,应该是高效率的生产,干净的车间,完美的自动化装配。一群怪兽来了,如此低效率产出超人是不现实,我们需要自动化 —— 最多一条指令,千军万马来相见。我们需要一种高级的生产车间,我们只需要向生产车间提交一个脚本,工厂便能够通过指令自动化生产。这种更为高级的工厂,就是工厂模式的升华 —— IoC 容器。class Container{ protected $binds; protected $instances; public function bind($abstract, $concrete) { if ($concrete instanceof Closure) { $this->binds[$abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } public function make($abstract, $parameters = []) { if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } array_unshift($parameters, $this); return call_user_func_array($this->binds[$abstract], $parameters); } }这时候,一个十分粗糙的容器就诞生了。现在的确很简陋,但不妨碍我们进一步提升他。先着眼现在,看看这个容器如何使用吧!// 创建一个容器(后面称作超级工厂)$container = new Container;// 向该 超级工厂 添加 超人 的生产脚本$container->bind('superman', function($container, $moduleName) { return new Superman($container->make($moduleName)); });// 向该 超级工厂 添加 超能力模组 的生产脚本$container->bind('xpower', function($container) { return new XPower; });// 同上$container->bind('ultrabomb', function($container) { return new UltraBomb; });// ****************** 华丽丽的分割线 **********************// 开始启动生产$superman_1 = $container->make('superman', ['xpower']);$superman_2 = $container->make('superman', ['ultrabomb']);$superman_3 = $container->make('superman', ['xpower']);// ...随意添加看到没?通过最初的 绑定(bind) 操作,我们向 超级工厂 注册了一些生产脚本,这些生产脚本在生产指令下达之时便会执行。发现没有?我们彻底的解除了 超人 与 超能力模组 的依赖关系,更重要的是,容器类也丝毫没有和他们产生任何依赖!我们通过注册、绑定的方式向容器中添加一段可以被执行的回调(可以是匿名函数、非匿名函数、类的方法)作为生产一个类的实例的 脚本 ,只有在真正的 生产(make) 操作被调用执行时,才会触发。这样一种方式,使得我们更容易在创建一个实例的同时解决其依赖关系,并且更加灵活。当有新的需求,只需另外绑定一个“生产脚本”即可。实际上,真正的 IoC 容器更为高级。我们现在的例子中,还是需要手动提供超人所需要的模组参数,但真正的 IoC 容器会根据类的依赖需求,自动在注册、绑定的一堆实例中搜寻符合的依赖需求,并自动注入到构造函数参数中去。Laravel 框架的服务容器正是这么做的。实现这种功能其实理论上并不麻烦,但我并不会在本文中写出,因为……我懒得写。不过我告诉大家,这种自动搜寻依赖需求的功能,是通过 反射(Reflection) 实现的,恰好的,php 完美的支持反射机制!关于反射,php 官方文档有详细的资料,并且中文翻译基本覆盖,足够学习和研究!http://php.net/manual/zh/book...现在,到目前为止,我们已经不再惧怕怪兽们了。高智商人才集思广益,井井有条,根据接口契约创造规范的超能力模组。超人开始批量产出。最终,人人都是超人,你也可以是哦!回归正常世界。我们开始重新审视 laravel 的核心。现在,我们开始慢慢解读 laravel 的核心。其实,laravel 的核心就是一个 IoC 容器,也恰好是我之前所说的高级的 IoC 容器。可以说,laravel 的核心本身十分轻量,并没有什么很神奇很实质性的应用功能。很多人用到的各种功能模块比如 Route(路由)、Eloquent ORM(数据库 ORM 组件)、Request and Response(请求和响应)等等等等,实际上都是与核心无关的类模块提供的,这些类从注册到实例化,最终被你所使用,其实都是 laravel 的服务容器负责的。我们以大家最常见的 Route 类作为例子。大家可能经常见到路由定义是这样的:Route::get('/', function() { // bla bla bla...});实际上, Route 类被定义在这个命名空间:Illuminate\Routing\Router,文件 vendor/laravel/framework/src/Illuminate/Routing/Router.php。我们通过打开发现,这个类的这一系列方法,如 get,post,any 等都不是静态(static)方法,这是怎么一回事儿?不要急,我们继续。服务提供者我们在前文介绍 IoC 容器的部分中,提到了,一个类需要绑定、注册至容器中,才能被“制造”。对,一个类要被容器所能够提取,必须要先注册至这个容器。既然 laravel 称这个容器叫做服务容器,那么我们需要某个服务,就得先注册、绑定这个服务到容器,那么提供服务并绑定服务至容器的东西,就是 服务提供者(ServiceProvider)。虽然,绑定一个类到容器不一定非要通过 服务提供者(ServiceProvider) 。但是,我们知道,有时候我们的类、模块会有需要其他类和组件的情况,为了保证初始化阶段不会出现所需要的模块和组件没有注册的情况,laravel 将注册和初始化行为进行拆分,注册的时候就只能注册,初始化的时候就是初始化。拆分后的产物就是现在的 服务提供者。服务提供者主要分为两个部分,register(注册) 和 boot(引导、初始化),具体参考文档。register 负责进行向容器注册“脚本”,但要注意注册部分不要有对未知事物的依赖,如果有,就要移步至 boot 部分。Facade我们现在解答之前关于 Route 的方法为何能以静态方法访问的问题。实际上这个问题文档上有写,简单说来就是模拟一个类,提供一个静态魔术方法__callStatic,并将该静态方法映射到真正的方法上。我们使用的 Route 类实际上是 Illuminate\Support\Facades\Route 通过 class_alias() 函数创造的 别名 而已,这个类被定义在文件 vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php 。我们打开文件一看……诶?怎么只有这么简单的一段代码呢?<?php namespace Illuminate\Support\Facades;/** * @see \Illuminate\Routing\Router */class Route extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'router'; } }其实仔细看,会发现这个类继承了一个叫做 Facade 的类,到这里谜底差不多要解开了。上述简单的定义中,我们看到了 getFacadeAccessor 方法返回了一个 route,这是什么意思呢?事实上,这个值被一个 ServiceProvider 注册过,大家应该知道注册了个什么,当然是那个真正的路由类!有人会问,Facade 是怎么实现的。我并不想说得太细,一个是我懒,另一个原因就是,自己发现一些东西更容易理解,并不容易忘记。很多细节我已经说了,建议大家自行去研究。至此,我们已经讲的差不多了。和平!我们该总结总结了!无论如何,世界和平了。这里要总结的内容就是,其实很多事情并不复杂,怕的是复杂的理论内容。我觉得很多东西一旦想通也就那么回事儿。很多人觉得 laravel 这不好那不好、这里难哪里难,我只能说,laravel 的确不是一流和优秀的框架,说 laravel 是一流、优秀的框架的人,不是 laravel 的粉丝那么就是跟风炒作。Laravel 最大的特点和优秀之处就是使用了很多 php 比较新(实际上并不新)的概念和技术(也就一堆语法糖)而已。因此 laravel 的确符合一个适宜学习的框架。Laravel 的构思的确和其他框架有很大不同,这也要求学习他的人必须熟练 php,并 基础扎实!如果你觉得学 laravel 框架十分困难,那么原因只有一个:你 php 基础不好。另外,善于利用命名空间和面向对象的诸多特性,去追寻一些东西,你会发现,原来这一切这么容易。
2021年04月26日
770 阅读
0 评论
0 点赞
2021-03-18
nginx 跨域设置
很多人都会遇到 Nginx 跨域的问题, 而我遇到的问题是:客户端在 www.a.com服务端在 www.b.comNginx 在 www.c.com此时需要对 Nginx 进行跨域配置才可以访问 www.c.com 的获取客户端 www.a.com 来请求 www.b.com 的数据, 我的 Nginx 配置如下(重要部分):location / { add_header 'Access-Control-Allow-Origin' $http_origin; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,web-token,app-token,Authorization,Accept,Origin,Keep-Alive,User-Agent,X-Mx-ReqToken,X-Data-Type,X-Auth-Token,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; if ($request_method = 'OPTIONS') { add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; } root html; proxy_pass http://xxx:8000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 5; }别忘了把配置中 proxy_pass 对应的 http://xxx:8000/ 地址换成你的服务地址, 当然了, 你的客户端请求的地址不可以是你的服务地址, 而是 Nginx 的地址, 这样就可以达到解决跨域的问题。
2021年03月18日
829 阅读
0 评论
0 点赞
1
2