Skip to main content

13 posts tagged with "php"

View All Tags

rabbitmq心跳问题和php

· One min read

为什么我们需要心跳

tcp 靠的是什么保证链接?

序列号和重传,这是传输层的事情,但是对于应用层来说,是感知不到对端断开的,所以需要应用层的心跳.

php的心跳有什么问题?

php大部分都是单进程模型,所以没有一个额外的线程去定时给这个tcp链接发一个心跳包,导致一旦运行比较长的时间(心跳时间*2),对端的rabbitmq会断开连接

所以大部分场景我们需要保证我们的运行时间小于心跳时间 , 不然会有pipe broken的问题,其实这个问题一般是超过心跳时间,导致rabbitmq 手动断开tcp连接了

一次tcp错误排查

· 2 min read

一个使用php的workman的代码抛出了这样的异常

fwrite(): send of 157 bytes failed with errno=11 Resource temporarily unavailable

socket是使用了noblocking的,结果有一个这个错误,结果发现是已经修复了不抛异常了,但是在我的php5.6的版本还是旧的代码,所以还是会抛这个异常,所以排查了一天,不是bug 最新的修改

https://github.com/php/php-src/pull/5026/files
	didwrite = send(sock->socket, buf, XP_SOCK_BUF_SIZE(count), (sock->is_blocked && ptimeout) ? MSG_DONTWAIT : 0);

if (didwrite <= 0) {
char *estr;
int err = php_socket_errno();
if (err == EWOULDBLOCK || err == EAGAIN) {
if (sock->is_blocked) {
int retval;

sock->timeout_event = 0;

do {
retval = php_pollfd_for(sock->socket, POLLOUT, ptimeout);

if (retval == 0) {
sock->timeout_event = 1;
break;
}

if (retval > 0) {
/* writable now; retry */
goto retry;
}

err = php_socket_errno();
} while (err == EINTR);
} else {
/* EWOULDBLOCK/EAGAIN is not an error for a non-blocking stream.
* Report zero byte write instead. */
return 0;
}
}

estr = php_socket_strerror(err, NULL, 0);
php_error_docref(NULL, E_NOTICE, "Send of " ZEND_LONG_FMT " bytes failed with errno=%d %s",
(zend_long)count, err, estr);
efree(estr);
}

if (didwrite > 0) {
php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), didwrite, 0);
}

return didwrite;
}

php opcode to handler

· One min read

php 的opcode 对应很多的handler

选哪个handler 是怎么选择的呢?

和tcp协议一个连接 是一个五元组一样

php的opcode 的handler 是一个三元组 分别是 opcode , op1 , op2

核心函数

ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler(zend_op* op)
{
zend_uchar opcode = zend_user_opcodes[op->opcode];

...
// zend_opcode_handlers 是什么呢? 是一堆函数指针的数组 ,每个opcode + op1+ op2 决定一个 函数指针
op->handler = zend_opcode_handlers[zend_vm_get_opcode_handler_idx(zend_spec_handlers[opcode], op)];
}

所以核心的核心就是

zend_vm_get_opcode_handler_idx(zend_spec_handlers[opcode], op)

他做了什么呢? 其实就是算出zend_opcode_handlers这个函数指针数组的偏移值

coding

· 2 min read

我很蠢,什么都不会,代码也不会写

我只是喜欢一个我不存在的东西

我觉得写工作的代码很恶心

但是我喜欢修bug

最近最开心的是给php-src提了两个pr并且通过了 ,但是我还是不会写php,我也不会c,我也背不过php的array系列的函数.

说到底,我还是太弱了

我其实不会写代码,但是会修bug.

因为修bug是体力活,写代码是脑力活

我果然很笨,什么都不会 只会写一堆bug

准备看看能不能修mysql的代码

没有银弹 No Silver Bullet

我真的会写代码吗? 我其实什么都不懂吧

我真的什么都不懂,有点怀疑自己这几年是不是只会copy and plaste 了

php 反射

· One min read

反射是一个很特别的api,php的反射是一个很特别的回调

(gdb) bt
#0 zim_reflection_class_hasProperty (execute_data=0x7ffff3a14220, return_value=0x7ffff3a14180) at /home/ubuntu/php-src-php-7.4.1/ext/reflection/php_reflection.c:4186
#1 0x0000555555af49b2 in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER () at /home/ubuntu/php-src-php-7.4.1/Zend/zend_vm_execute.h:1729
#2 0x0000555555b58295 in execute_ex (ex=0x7ffff3a14020) at /home/ubuntu/php-src-php-7.4.1/Zend/zend_vm_execute.h:53588
#3 0x0000555555b5c32d in zend_execute (op_array=0x7ffff3a61c00, return_value=0x0) at /home/ubuntu/php-src-php-7.4.1/Zend/zend_vm_execute.h:57664
#4 0x0000555555a80b27 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /home/ubuntu/php-src-php-7.4.1/Zend/zend.c:1663
#5 0x00005555559e2bad in php_execute_script (primary_file=0x7fffffffd0e0) at /home/ubuntu/php-src-php-7.4.1/main/main.c:2619
#6 0x0000555555b5ee34 in do_cli (argc=2, argv=0x55555678ab30) at /home/ubuntu/php-src-php-7.4.1/sapi/cli/php_cli.c:961
#7 0x0000555555b5ff9e in main (argc=2, argv=0x55555678ab30) at /home/ubuntu/php-src-php-7.4.1/sapi/cli/php_cli.c:1352

php pdo 相关参数

· One min read

thinkphp5 的默认配置会开启ERRMODE_EXCEPTION

        PDO::ATTR_CASE              => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false,

pdo实现

pdo 的raise_impl_error会根据配置判断是否需要抛出异常,当设置成PDO::ERRMODE_EXCEPTION,则可以需要抛出异常

void pdo_raise_impl_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *sqlstate, const char *supp) /* {{{ */
{
...
if (dbh && dbh->error_mode != PDO_ERRMODE_EXCEPTION) { // 没有设置 ERRMODE_EXCEPTION则抛warning
php_error_docref(NULL, E_WARNING, "%s", message);
} else {
zval ex, info;
zend_class_entry *def_ex = php_pdo_get_exception_base(1), *pdo_ex = php_pdo_get_exception();

object_init_ex(&ex, pdo_ex);

zend_update_property_string(def_ex, &ex, "message", sizeof("message")-1, message);
zend_update_property_string(def_ex, &ex, "code", sizeof("code")-1, *pdo_err);

array_init(&info);

add_next_index_string(&info, *pdo_err);
add_next_index_long(&info, 0);
zend_update_property(pdo_ex, &ex, "errorInfo", sizeof("errorInfo")-1, &info);
zval_ptr_dtor(&info);

zend_throw_exception_object(&ex); // // 否则抛出异常
}

if (message) {
efree(message);
}
}

所以sql相关的错误只要try_catch还是能catch不少的

协程切换

· One min read
Breakpoint 2, 0x00007fffedb6c090 in swoole::Context::Context(unsigned long, void (*)(void*), void*)@plt ()
from /usr/local/phpfork/lib/php/extensions/debug-non-zts-20170718/swoole.so
(gdb) bt
#0 0x00007fffedb6c090 in swoole::Context::Context(unsigned long, void (*)(void*), void*)@plt ()
from /usr/local/phpfork/lib/php/extensions/debug-non-zts-20170718/swoole.so
#1 0x00007fffedc207b1 in swoole::Coroutine::Coroutine (this=0x17a8540, fn=0x7fffedc1cdb2 <swoole::PHPCoroutine::main_func(void*)>, private_data=0x7fffffffa0a0)
at /home/dinosaur/swoole-src/include/coroutine.h:204
#2 0x00007fffedc205ee in swoole::Coroutine::create (fn=0x7fffedc1cdb2 <swoole::PHPCoroutine::main_func(void*)>, args=0x7fffffffa0a0)
at /home/dinosaur/swoole-src/include/coroutine.h:121
#3 0x00007fffedc1d7d0 in swoole::PHPCoroutine::create (fci_cache=0x7fffffffa140, argc=0, argv=0x0) at /home/dinosaur/swoole-src/swoole_coroutine.cc:857
#4 0x00007fffedc1eebd in zif_swoole_coroutine_create (execute_data=0x7fffef61e090, return_value=0x7fffffffa1e0)
at /home/dinosaur/swoole-src/swoole_coroutine.cc:964
#5 0x0000000000aaf137 in ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_UNUSED_HANDLER () at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_vm_execute.h:738
#6 0x0000000000b42992 in execute_ex (ex=0x7fffef61e030) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_vm_execute.h:59743
#7 0x0000000000b47d9d in zend_execute (op_array=0x7fffef684b00, return_value=0x0) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_vm_execute.h:63760
#8 0x0000000000a3afe0 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend.c:1496
#9 0x000000000098c749 in php_execute_script (primary_file=0x7fffffffc8c0) at /home/dinosaur/Downloads/php-7.2.2/main/main.c:2590
#10 0x0000000000b4b2a5 in do_cli (argc=2, argv=0x1561f20) at /home/dinosaur/Downloads/php-7.2.2/sapi/cli/php_cli.c:1011
#11 0x0000000000b4c491 in main (argc=2, argv=0x1561f20) at /home/dinosaur/Downloads/php-7.2.2/sapi/cli/php_cli.c:1404

php tokenlizer与php-cs-fixer

· 2 min read

tokenlizer

代码:

<?php
$code = '<?php echo "string1"."string2"; >';

$tokens = token_get_all($code);

foreach ($tokens as $token) {

if (is_array($token)) {

// 行号、标识符字面量、对应内容

printf("%d - %s\t%s\n", $token[2], token_name($token[0]), $token[1]);

}

}

输出内容:

1 - T_OPEN_TAG  <?php
1 - T_ECHO echo
1 - T_WHITESPACE
1 - T_CONSTANT_ENCAPSED_STRING "string1"
1 - T_CONSTANT_ENCAPSED_STRING "string2"
1 - T_WHITESPACE

php-cs-fixer

php-cs-fixer的核心函数是:token_get_all

        $tokens = \defined('TOKEN_PARSE')
? token_get_all($code, TOKEN_PARSE)
: token_get_all($code);

调用的核心堆栈:

#0 E:\PHP-CS-Fixer\src\Tokenizer\Tokens.php(222): PhpCsFixer\Tokenizer\Tokens->setCode('<?php\n\n/*\n * Th...')
#1 E:\PHP-CS-Fixer\src\Runner\Runner.php(171): PhpCsFixer\Tokenizer\Tokens::fromCode('<?php\n\n/*\n * Th...')
#2 E:\PHP-CS-Fixer\src\Runner\Runner.php(132): PhpCsFixer\Runner\Runner->fixFile(Object(SplFileInfo), Object(PhpCsFixer\Linter\ProcessLintingResult))
#3 E:\PHP-CS-Fixer\src\Console\Command\FixCommand.php(219): PhpCsFixer\Runner\Runner->fix()
#4 E:\PHP-CS-Fixer\vendor\symfony\console\Command\Command.php(255): PhpCsFixer\Console\Command\FixCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#5 E:\PHP-CS-Fixer\vendor\symfony\console\Application.php(982): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#6 E:\PHP-CS-Fixer\vendor\symfony\console\Application.php(255): Symfony\Component\Console\Application->doRunCommand(Object(PhpCsFixer\Console\Command\FixCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#7 E:\PHP-CS-Fixer\src\Console\Application.php(84): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#8 E:\PHP-CS-Fixer\vendor\symfony\console\Application.php(148): PhpCsFixer\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#9 E:\PHP-CS-Fixer\php-cs-fixer(101): Symfony\Component\Console\Application->run()
#10 {main}

举个例子下面的堆栈:

#0 E:\PHP-CS-Fixer\src\Fixer\Operator\BinaryOperatorSpacesFixer.php(339): PhpCsFixer\Fixer\Operator\BinaryOperatorSpacesFixer->fixWhiteSpaceAroundOperatorToSingleSpace(Object(PhpCsFixer\Tokenizer\Tokens), 19)
#1 E:\PHP-CS-Fixer\src\Fixer\Operator\BinaryOperatorSpacesFixer.php(256): PhpCsFixer\Fixer\Operator\BinaryOperatorSpacesFixer->fixWhiteSpaceAroundOperator(Object(PhpCsFixer\Tokenizer\Tokens), 19)
#2 E:\PHP-CS-Fixer\src\AbstractFixer.php(75): PhpCsFixer\Fixer\Operator\BinaryOperatorSpacesFixer->applyFix(Object(SplFileInfo), Object(PhpCsFixer\Tokenizer\Tokens))
#3 E:\PHP-CS-Fixer\src\Runner\Runner.php(192): PhpCsFixer\AbstractFixer->fix(Object(SplFileInfo), Object(PhpCsFixer\Tokenizer\Tokens))
#4 E:\PHP-CS-Fixer\src\Runner\Runner.php(132): PhpCsFixer\Runner\Runner->fixFile(Object(SplFileInfo), Object(PhpCsFixer\Linter\ProcessLintingResult))
#5 E:\PHP-CS-Fixer\src\Console\Command\FixCommand.php(219): PhpCsFixer\Runner\Runner->fix()
#6 E:\PHP-CS-Fixer\vendor\symfony\console\Command\Command.php(255): PhpCsFixer\Console\Command\FixCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#7 E:\PHP-CS-Fixer\vendor\symfony\console\Application.php(982): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#8 E:\PHP-CS-Fixer\vendor\symfony\console\Application.php(255): Symfony\Component\Console\Application->doRunCommand(Object(PhpCsFixer\Console\Command\FixCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#9 E:\PHP-CS-Fixer\src\Console\Application.php(84): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 E:\PHP-CS-Fixer\vendor\symfony\console\Application.php(148): PhpCsFixer\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#11 E:\PHP-CS-Fixer\php-cs-fixer(101): Symfony\Component\Console\Application->run()
#12 {main}

核心就是给后面加入token // todo

composer-ext

· One min read

看到phpstorm的相关警告,经常会看到phpstorm会警告没有ext-json,我才最近发现composer.json会添加相关的扩展校验.

举个例子

    "require": {
"php": ">=5.4.0",
"topthink/framework": "^5.0",
"php-imap/php-imap": "~2.0",
"phpoffice/phpspreadsheet": "^1.3",
"hprose/hprose": "^2.0",
"ext-json": "*" // 这就是解析require json 扩展
},

这个就是校验是否含有json扩展,那么composer是怎么实现的呢?其实是通过extension_loaded这个函数取查看扩展版本的

实现是在composer的129行实现,通过extension_loaded获取扩展.

相关阅读