问题
在PHPunit断点调试时,发现其中的 echo,print 语句时,不会立即输出,而是在测试脚本完成时才有全部输出。为了能够立马输出,在调试时,执行一行就输出一行,需要使用如下代码。
输出代码
<?php
fwrite(STDERR, print_r("\n" . memory_get_usage(), TRUE));
echo "\n" . memory_get_usage();
又名:IT技术站
单元测试,echo输出没东西?
在PHPunit断点调试时,发现其中的 echo,print 语句时,不会立即输出,而是在测试脚本完成时才有全部输出。为了能够立马输出,在调试时,执行一行就输出一行,需要使用如下代码。
<?php
fwrite(STDERR, print_r("\n" . memory_get_usage(), TRUE));
echo "\n" . memory_get_usage();
匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数。最经常用作回调函数(callback)参数的值。当然,也有其它应用的情况。
匿名函数目前是通过 Closure 类来实现的。
Example #1 匿名函数示例
<?php
echo preg_replace_callback('~-([a-z])~', function ($match) {
return strtoupper($match[1]);
}, 'hello-world');
// 输出 helloWorld
?>
闭包函数也可以作为变量的值来使用。PHP 会自动把此种表达式转换成内置类 Closure 的对象实例。把一个 closure 对象赋值给一个变量的方式与普通变量赋值的语法是一样的,最后也要加上分号:
Example #2 匿名函数变量赋值示例
<?php
$greet = function($name)
{
printf("Hello %s\r\n", $name);
};
$greet('World');
$greet('PHP');
?>
闭包可以从父作用域中继承变量。 任何此类变量都应该用 use 语言结构传递进去。 PHP 7.1 起,不能传入此类变量: superglobals、 $this 或者和参数重名。
Example #3 从父作用域继承变量
<?php
$message = 'hello';
// 没有 "use"
$example = function () {
var_dump($message);
};
echo $example();
// 继承 $message
$example = function () use ($message) {
var_dump($message);
};
echo $example();
// Inherited variable's value is from when the function
// is defined, not when called
$message = 'world';
echo $example();
// Reset message
$message = 'hello';
// Inherit by-reference
$example = function () use (&$message) {
var_dump($message);
};
echo $example();
// The changed value in the parent scope
// is reflected inside the function call
$message = 'world';
echo $example();
// Closures can also accept regular arguments
$example = function ($arg) use ($message) {
var_dump($arg . ' ' . $message);
};
$example("hello");
?>
<?php
/**
* author: selfimpr
* mail: lgg860911@yahoo.com.cn
* blog: http://blog.csdn.NET/lgg201
* 下面提到的代码在PHP5.3以上版本运行通过.
*/
function callback($callback) {
$callback();
}
//输出: This is a anonymous function.<br />/n
//这里是直接定义一个匿名函数进行传递, 在以往的版本中, 这是不可用的.
//现在, 这种语法非常舒服, 和JavaScript语法基本一致, 之所以说基本呢, 需要继续向下看
//结论: 一个舒服的语法必然会受欢迎的.
callback(function() {
print "This is a anonymous function.<br />/n";
});
//输出: This is a closure use string value, msg is: Hello, everyone.<br />/n
//这里首先定义了一个闭包, 这次户口本上有名字了...
//use, 一个新鲜的家伙...
//众所周知, 闭包: 内部函数使用了外部函数中定义的变量.
//在PHP新开放的闭包语法中, 我们就是用use来使用闭包外部定义的变量的.
//这里我们使用了外部变量$msg, 定义完之后, 又对其值进行了改变, 闭包被执行后输出的是原始值
//结论: 以传值方式传递的基础类型参数, 闭包use的值在闭包创建是就确定了.
$msg = "Hello, everyone";
$callback = function () use ($msg) {
print "This is a closure use string value, msg is: $msg. <br />/n";
};
$msg = "Hello, everybody";
callback($callback);
//输出: This is a closure use string value lazy bind, msg is: Hello, everybody.<br />/n
//换一种引用方式, 我们使用引用的方式来use
//可以发现这次输出是闭包定义后的值...
//这个其实不难理解, 我们以引用方式use, 那闭包use的是$msg这个变量的地址
//当后面对$msg这个地址上的值进行了改变之后, 闭包内再输出这个地址的值时, 自然改变了.
$msg = "Hello, everyone";
$callback = function () use (&$msg) {
print "This is a closure use string value lazy bind, msg is: $msg. <br />/n";
};
$msg = "Hello, everybody";
callback($callback);
//输出: This is a closure use object, msg is: Hello, everyone.<br />/n
//闭包中输出的是之前被拷贝的值为Hello, everyone的对象, 后面是对$obj这个名字的一个重新赋值.
//可以这样考虑
//1. obj是对象Hello, everyone的名字
//2. 对象Hello, everyone被闭包use, 闭包产生了一个对Hello, everyone对象的引用
//3. obj被修改为Hello, everybody这个对象的名字
//4. 注意, 是名字obj代表的实体变了, 而不是Hello, everyone对象, 那自然闭包的输出还是前面的Hello, everyone
$obj = (object) "Hello, everyone";
$callback = function () use ($obj) {
print "This is a closure use object, msg is: {$obj->scalar}. <br />/n";
};
$obj = (object) "Hello, everybody";
callback($callback);
//输出: This is a closure use object, msg is: Hello, everybody.<br />/n
//还是按照上面的步骤, 按部就班的来吧:
//1. obj名字指向Hello, everyone对象
//2. 闭包产生一个引用指向Hello, everyone对象
//3. 修改obj名字指向的对象(即Hello, everyone对象)的scalar值
//4. 执行闭包, 输出的自然是Hello, everybody, 因为其实只有一个真正的对象
$obj = (object) "Hello, everyone";
$callback = function () use ($obj) {
print "This is a closure use object, msg is: {$obj->scalar}. <br />/n";
};
$obj->scalar = "Hello, everybody";
callback($callback);
//输出: This is a closure use object lazy bind, msg is: Hello, everybody.<br />/n
//闭包引用的是什么呢? &$obj, 闭包产生的引用指向$obj这个名字所指向的地址.
//因此, 无论obj怎么变化, 都是逃不脱的....
//所以, 输出的就是改变后的值
$obj = (object) "Hello, everyone";
$callback = function () use (&$obj) {
print "This is a closure use object lazy bind, msg is: {$obj->scalar}. <br />/n";
};
$obj = (object) "Hello, everybody";
callback($callback);
/**
* 一个利用闭包的计数器产生器
* 这里其实借鉴的是Python中介绍闭包时的例子...
* 我们可以这样考虑:
* 1. counter函数每次调用, 创建一个局部变量$counter, 初始化为1.
* 2. 然后创建一个闭包, 闭包产生了对局部变量$counter的引用.
* 3. 函数counter返回创建的闭包, 并销毁局部变量, 但此时有闭包对$counter的引用,
* 它并不会被回收, 因此, 我们可以这样理解, 被函数counter返回的闭包, 携带了一个游离态的
* 变量.
* 4. 由于每次调用counter都会创建独立的$counter和闭包, 因此返回的闭包相互之间是独立的.
* 5. 执行被返回的闭包, 对其携带的游离态变量自增并返回, 得到的就是一个计数器.
* 结论: 此函数可以用来生成相互独立的计数器.
*/
function counter() {
$counter = 1;
return function() use(&$counter) {return $counter ++;};
}
$counter1 = counter();
$counter2 = counter();
echo "counter1: " . $counter1() . "<br />/n";
echo "counter1: " . $counter1() . "<br />/n";
echo "counter1: " . $counter1() . "<br />/n";
echo "counter1: " . $counter1() . "<br />/n";
echo "counter2: " . $counter2() . "<br />/n";
echo "counter2: " . $counter2() . "<br />/n";
echo "counter2: " . $counter2() . "<br />/n";
echo "counter2: " . $counter2() . "<br />/n";
?>
反射一个类
//反射一个类
$obj = new \App\DataProviders\RightScale(['cloud'=>3,'deployment'=>'']);
$observer = new \ReflectionClass($obj);
//反射一个属性
$property = $observer->getProperty('_instance');
$property->setAccessible(true);
$property->setValue($obj,'DHP9CG78PUVFA');
//反射一个方法
$method = $observer->getMethod('getCpuUsage');
$method->setAccessible(true);
$re = $method->invoke($obj, []);
github ProtoBuf,这里选择cpp
版本。
./configure --disable-shared x86_64-apple-darwin17.7.0 --prefix=/Users/paulxu/Jumei/compose/PicsToPdf/gcc/protobuf-build/
## make clean # optional
make -j2 # 双倍编译
make install
export PKG_CONFIG_PATH="/Users/paulxu/Jumei/compose/PicsToPdf/gcc/protobuf-3.7.1"
g++ `pkg-config --cflags --libs protobuf` -std=c++11 -v ./lm.helloworld.pb.cc ./proto_test1.cpp -o proto_test1.o
syntax = "proto3";
package Demo;
message Files {
repeated File file = 1;
}
message File {
string fileName = 1;
uint32 fileSize = 2;
string ext = 3;
bytes data = 4;
}
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto
方案:这个是*.proto
文件语法问题,如果你下载ProtoBuf3.*
版本,为了和ProtoBuf2.*
区别开来,就会有这样的提示,要求你在第一行加上提示信息就行了。
方案:新版本ProtoBuf
基于C++11
,使用了新特性以及语法,所以在编译测试代码时,需要加上-std=c++11
就行了。
方案:这个是包include路径配置不完整,需要完善。
方案:编译时,需要把 *.pb.c
就是模版文件生成的目标文件带上。
最详细的保姆级别的Dcoker环境想配置PHPStorm Xdebug 断点调试教程,图文并茂
PHP
程序报错,肉眼review了多少遍都觉得自己的写得没问题;有个switch代码分支判断,为什么进不了我想要的case
; 调试PHP
代码还在用 var_dump($re); die();
。试试Xdebug
断点调试吧。
开启Debug监听,一步步顺着代码走进程序的最深处。你会了解到真实的代码运行步骤,以及调用关系。你还能知道每个变量在程序运行时,值的多少,以及变化。同时避免了 var_dump
代码植入,(如果忘了删除,提交到线上,那这个耻辱柱会狠狠地钉在你身上)
好的来介绍下我们的这期主角 Xdebug
Xdebug是一个PHP扩展,提供了调试和性能分析功能。[1]它使用DBGp调试协议。
Xdebug可以提供的调试信息包括以下内容:
Xdebug还提供:
以上摘自wiki
这篇文章主要围绕Docker环境下PHP的Xdebug调试展开。
先说下笔者演示环境:Mac环境,安装Virtual Box,跑了一个Ubuntu虚拟机,再装了Docker ce软件。看到这,大伙儿先别急着关网页啊,我的环境估计有些奇葩,但大致流程是差不多的。
这套PHP Docker
环境这篇文章有详细介绍: [Docker快速搭建一套PHP、Nginx、MySQL、Redis、Xdebug、Memcached 开发环境
Xdebug 官方文档已介绍。
$ pecl install xdebug
*.dll
放入到拓展目录即可。$ php -v // 查看 PHP 版本
对于网络不那么畅通,pecl不能用时。
## 获取源码
$ git clone https://github.com/xdebug/xdebug.git
## 进入目录
$ cd xdebug
## php检查
$ phpize
## 编译前配置,一般来说 phpize 已经准备好了大部分工作,以及配置
$ ./configure
## 编译
$ make
## 测试并安装
$ make test && make install
Docker 环境下提供了两种方式,第一种是通过pecl在线下载安装,受限于网络状况,大概了下会失败。
那么可以尝试通过其他途径下载好pecl-Xdebug的压缩包,Add到docker容器中安装。
FROM php:7.3-fpm-buster
...
# 方法1 pecl 安装
RUN yes | pecl install xdebug \
&& echo "zend_extension=$(find /usr/local/lib/php/extensions/ -name xdebug.so)" > /usr/local/etc/php/conf.d/xdebug.ini \
&& echo "xdebug.remote_enable=on" >> /usr/local/etc/php/conf.d/xdebug.ini \
&& echo "xdebug.remote_autostart=off" >> /usr/local/etc/php/conf.d/xdebug.ini
## ------------------------------------------------
# 方法2 下载安装 xdebug
ADD ./xdebug-2.6.1.tgz /tmp/php7-xdebug
RUN cd /tmp/php7-xdebug/xdebug-2.6.1/ && phpize && ./configure && \
make && make install
找到我们的配置文件.
## 文件: /etc/php/conf.d/xdebug.ini
zend_extension=xdebug.so // 启用xdebug拓展!
xdebug.remote_connect_back=1
xdebug.remote_enable = 1 // 启用远程调试
xdebug.remote_mode = "req"
xdebug.remote_handler = "dbgp"
xdebug.remote_host = "10.0.2.2" // 这是你PHPStorm运行环境的IP地址
xdebug.remote_port = 9000 // 默认9000,最好别动
xdebug.idekey="PHPSTORM" // PHPStormIDE用的默认值
xdebug.remote_autostart = 1
关于这个 xdebug.remote_host
这个ip,不是想当然就填个 127.0.0.1
, 你要看看你PHPStorm所运行的环境,和PHP运行环境是否在一个环境,如果是,你填 127.0.0.1
是没问题。是Docker那么就肯定不在一个环境了,怎么看我们IDE所处环境的IP呢,先编辑下入口文件 index.php
, 获取SERVER
变量的 REMOTE_ADDR
属性。
## 文件: ~/public/index.php
<?php
print_r($_SERVER['REMOTE_ADDR']);die();
打开浏览器访问下php-fpm服务。这里是多少,xdebug.remote_host
就填多少。
Docker环境下,要用IDE去debug代码,IDE还需要和Docker打交道。就是在PHPStorm配置好Dcoker的API入口。
这个大家可以参考这片文章 Docker开启Remote API 访问 2375端口 来配置下Docker环境。
转载注明出处
以上准备好了后,就开始我们的重头戏,配置PHPStorm
.
1.将Docker Remote API
配置在PHPStorm上.
由于我的环境装了Vbox,用了NAT,所以端口是42376,你要自己的环境配置为准。
下面的 Connection Successful
就表示配置成功了。这里的 Name:
名字要记下来,后面要用。
2.新增PHP Cli Interpreter
.
搜索: php language,点击最右侧 ···
按钮
选择 From Dcoker...
这里的 Server:
名字选择 第 1 步
的 Dcoker Server 的名字。 Image name:
选择你的php容器。
点击确认后,PHPStorm会检查 PHP容器的版本,Xdebug拓展是否启用。有如下提示,没有报错,则表示环境无误,可进行下一步。
留意下红框里面的
PHP version:
这里就是配置好之后的展示效果,PHP language level:
最好和 PHP 容器内一致.
3.配置 Deployment
这个配置是用来让IDE知道PHP代码运行环境,以及本地PHP代码之间的目录映射关系。
这里选择 Local or mounted flolder
,首先会让你输入 New server name:
。
我输入的是
bs
。你也可以输入别的,但都要记录下来,后面会用到这个名字。
这里Folder:
配置PHPStorm运行环境的文件路径。(也就是Mac下文件路径。)
这里我们查看下 PHP 容器内代码路径.
切到 Deployemnt
配置的 Mapping
Tab,在 Deploment path:
填上容器内代码路径, Local path:
选择本地环境代码路径。
4.配置PHP > Server
, 这里点击圆圈内的 导入 按钮。在弹框中选择刚刚新增的 Deployment
配置。(就是刚刚输入的 New server name
)
File/Direcotry
你检查下,一般都没问题。Absolute path on the server
是你代码运行的Docker容器内的根目录。调整好之后,点击 OK
.
先别急,关闭弹框后,再确认下 Absolute path on the server
。
这里估计是 PHPStorm 的一个Bug 还是产品的需求,弹框里外都要配置一次才好。
好PHPStorm的配置就完成了。
别急
Docker 运行时容器内,还需配置两个环境变量 PHP_IDE_CONFIG
, XDEBUG_CONFIG
.
serverName
写 Deployment
配置的名字。
remote_host
填PHPStorm环境的IP,也就是PHP中 $_SERVER['REMOTE_ADDR']
的值。
remote_port
默认 900.
docker-compose.yaml
配置文件中的 php 容器新增环境变量:## 文件: ~/docker-compose.yaml
version: "2"
services:
php:
image: paulxu/php:5.6-fpm-jessis-pdo-xdebug-mysqli-gd-mb-zip-2
volumes:
...
ports:
...
environment:
PHP_IDE_CONFIG: "serverName=bs"
XDEBUG_CONFIG: "remote_host=10.0.2.2 remote_port=9000"
docker run
拉起的容器,加上如下参数$ docker run ... --env PHP_IDE_CONFIG="serverName=dealman" --env PHP_IDE_CONFIG="serverName=bs" ... php /bin/bash
1.启用Xdebug listening,点击以下红色电话。
现在就是开始侦听状态。
2.找到入口文件 ~/index.php
,选择第一行代码,打一个断点,(不知道点哪,按Commond+F8)
3.开始访问Web站点。
这里第一个断点是红色箭头处,通过点击绿色按钮 Step over
,代码一步步执行到了蓝色箭头处,这行代码也被IDE蓝色高亮标底,此时还能从 Variables
看到 已定义变量 $conf
的值。
以前的大佬还在炫耀用notepad手撕代码,一遍过。在越来越快的开发节奏中,使用高级的工具,以及详尽的仪表盘来开发调试代码。希望大家看了,都能配置成功。
有问题欢迎提问。
vscode用xdebug调试php多进程程序
Mac下基于Docker在PhpStorm中配置Xdebug
使用 Xdebug 在 PHPStorm 中调试 PHP 程序(框架/原生均适用)