mirror of
https://github.com/php/doc-ru.git
synced 2026-03-23 23:32:16 +01:00
380 lines
9.1 KiB
XML
380 lines
9.1 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
||
<!-- EN-Revision: e14fdcab82e89bfb4ee221b1de9f4764c93abcf5 Maintainer: rjhdby Status: ready -->
|
||
<!-- Reviewed: no -->
|
||
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="ffi.examples">
|
||
&reftitle.examples;
|
||
<section xml:id="ffi.examples-basic">
|
||
<title>Базовая работа с модулем FFI</title>
|
||
<simpara>
|
||
Перед погружением в детали API-интерфейса модуля FFI, давайте рассмотрим
|
||
несколько примеров упрощённой работы API модуля FFI
|
||
в стандартных задачах.
|
||
</simpara>
|
||
<note>
|
||
<simpara>
|
||
Для ряда этих примеров потребуется
|
||
библиотека <filename>libc.so.6</filename>. Они не будут
|
||
работать в системах, где библиотека недоступна.
|
||
</simpara>
|
||
</note>
|
||
<example xml:id="ffi.examples.function">
|
||
<title>Вызов функции из общей библиотеки</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
// Создаём объект FFI, загружаем библиотеку libc и экспортируем функцию printf()
|
||
$ffi = FFI::cdef(
|
||
"int printf(const char *format, ...);", // Это стандартная декларация в языке C
|
||
"libc.so.6"
|
||
);
|
||
|
||
// Вызываем функцию printf()
|
||
$ffi->printf("Привет, %s!\n", "мир");
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
Привет, мир!
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
<note>
|
||
<simpara>
|
||
Обратите внимание, что для отдельных функций языка C требуются конкретные
|
||
соглашения о вызовах, например: <literal>__fastcall</literal>,
|
||
<literal>__stdcall</literal> или <literal>,__vectorcall</literal>.
|
||
</simpara>
|
||
</note>
|
||
<example xml:id="ffi.examples.structure">
|
||
<title>Вызов функции и возврат структуры через аргумент</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
// Создаём привязку gettimeofday()
|
||
$ffi = FFI::cdef(
|
||
"typedef unsigned int time_t;
|
||
typedef unsigned int suseconds_t;
|
||
|
||
struct timeval {
|
||
time_t tv_sec;
|
||
suseconds_t tv_usec;
|
||
};
|
||
|
||
struct timezone {
|
||
int tz_minuteswest;
|
||
int tz_dsttime;
|
||
};
|
||
|
||
int gettimeofday(struct timeval *tv, struct timezone *tz);",
|
||
"libc.so.6"
|
||
);
|
||
|
||
// Создаём структуры данных языка C
|
||
$tv = $ffi->new("struct timeval");
|
||
$tz = $ffi->new("struct timezone");
|
||
|
||
// Вызываем функцию gettimeofday()
|
||
var_dump($ffi->gettimeofday(FFI::addr($tv), FFI::addr($tz)));
|
||
|
||
// Получаем доступ к полю структуры данных языка C
|
||
var_dump($tv->tv_sec);
|
||
|
||
// Выводим структуру данных
|
||
var_dump($tz);
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs.similar;
|
||
<screen>
|
||
<![CDATA[
|
||
int(0)
|
||
int(1555946835)
|
||
object(FFI\CData:struct timezone)#3 (2) {
|
||
["tz_minuteswest"]=>
|
||
int(0)
|
||
["tz_dsttime"]=>
|
||
int(0)
|
||
}
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
<example xml:id="ffi.examples.variable-existing">
|
||
<title>Доступ к существующим переменным C</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
// Создаём FFI-объект, загружаем библиотеку libc и экспортируем переменную errno
|
||
$ffi = FFI::cdef(
|
||
"int errno;", // Это стандартная декларация в языке C
|
||
"libc.so.6"
|
||
);
|
||
|
||
// Выводим значение переменной errno
|
||
var_dump($ffi->errno);
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
int(0)
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
<example xml:id="ffi.examples.variable-creating">
|
||
<title>Создание и модификация C-переменной</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
// Создаём переменную языка C с типом int
|
||
$x = FFI::new("int");
|
||
var_dump($x->cdata);
|
||
|
||
// Простое присваивание
|
||
$x->cdata = 5;
|
||
var_dump($x->cdata);
|
||
|
||
// Непростое присваивание
|
||
$x->cdata += 2;
|
||
var_dump($x->cdata);
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
int(0)
|
||
int(5)
|
||
int(7)
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
<para>
|
||
<example xml:id="ffi.examples.array">
|
||
<title>Работа с массивами языка C</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
// Создаём структуру данных
|
||
$a = FFI::new("long[1024]");
|
||
|
||
// Работаем с ней как с обычным PHP-массивом
|
||
for ($i = 0; $i < count($a); $i++) {
|
||
$a[$i] = $i;
|
||
}
|
||
|
||
var_dump($a[25]);
|
||
|
||
$sum = 0;
|
||
foreach ($a as $n) {
|
||
$sum += $n;
|
||
}
|
||
|
||
var_dump($sum);
|
||
var_dump(count($a));
|
||
var_dump(FFI::sizeof($a));
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
int(25)
|
||
int(523776)
|
||
int(1024)
|
||
int(8192)
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
</para>
|
||
<example xml:id="ffi.examples.enum">
|
||
<title>Работа с перечислениями языка C</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
$a = FFI::cdef('typedef enum _zend_ffi_symbol_kind {
|
||
ZEND_FFI_SYM_TYPE,
|
||
ZEND_FFI_SYM_CONST = 2,
|
||
ZEND_FFI_SYM_VAR,
|
||
ZEND_FFI_SYM_FUNC
|
||
} zend_ffi_symbol_kind;
|
||
');
|
||
|
||
var_dump($a->ZEND_FFI_SYM_TYPE);
|
||
var_dump($a->ZEND_FFI_SYM_CONST);
|
||
var_dump($a->ZEND_FFI_SYM_VAR);
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
int(0)
|
||
int(2)
|
||
int(3)
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
</section>
|
||
|
||
<section xml:id="ffi.examples-callback">
|
||
<title>Callback-функции PHP</title>
|
||
<para>
|
||
Можно присвоить замыкание PHP переменной с типом «указатель» (pointer)
|
||
или передать замыкание как аргумент функции:
|
||
<example>
|
||
<title>Присваивание <classname>Closure</classname> языка PHP указателю функции языка C</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
$zend = FFI::cdef("
|
||
typedef int (*zend_write_func_t)(const char *str, size_t str_length);
|
||
extern zend_write_func_t zend_write;
|
||
");
|
||
|
||
echo "Привет, мир 1!\n";
|
||
|
||
$orig_zend_write = clone $zend->zend_write;
|
||
|
||
$zend->zend_write = function($str, $len) {
|
||
global $orig_zend_write;
|
||
$orig_zend_write("{\n\t", 3);
|
||
$ret = $orig_zend_write($str, $len);
|
||
$orig_zend_write("}\n", 2);
|
||
return $ret;
|
||
};
|
||
|
||
echo "Привет, мир 2!\n";
|
||
$zend->zend_write = $orig_zend_write;
|
||
echo "Привет, мир 3!\n";
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
Привет, мир 1!
|
||
{
|
||
Привет, мир 2!
|
||
}
|
||
Привет, мир 3!
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
Хоть это и работает, но эта функциональность
|
||
поддерживается не на каждой платформе с библиотекой libffi, не эффективна
|
||
и вызывает утечку ресурсов при завершении запросов.
|
||
<tip>
|
||
<simpara>
|
||
Поэтому рекомендуют минимизировать использование callback-функций PHP.
|
||
</simpara>
|
||
</tip>
|
||
</para>
|
||
</section>
|
||
|
||
<section xml:id="ffi.examples-complete">
|
||
<title>Комплексный пример PHP/FFI/preloading</title>
|
||
<informalexample>
|
||
<simpara><filename>php.ini</filename></simpara>
|
||
<programlisting role="ini">
|
||
<![CDATA[
|
||
ffi.enable=preload
|
||
opcache.preload=preload.php
|
||
]]>
|
||
</programlisting>
|
||
<simpara><filename>preload.php</filename></simpara>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
FFI::load(__DIR__ . "/dummy.h");
|
||
opcache_compile_file(__DIR__ . "/dummy.php");
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
<simpara><filename>dummy.h</filename></simpara>
|
||
<programlisting role="c">
|
||
<![CDATA[
|
||
#define FFI_SCOPE "DUMMY"
|
||
#define FFI_LIB "libc.so.6"
|
||
|
||
int printf(const char *format, ...);
|
||
]]>
|
||
</programlisting>
|
||
<simpara><filename>dummy.php</filename></simpara>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
final class Dummy
|
||
{
|
||
private static $ffi = null;
|
||
|
||
function __construct()
|
||
{
|
||
if (is_null(self::$ffi)) {
|
||
self::$ffi = FFI::scope("DUMMY");
|
||
}
|
||
}
|
||
|
||
function printf($format, ...$args)
|
||
{
|
||
return (int) self::$ffi->printf($format, ...$args);
|
||
}
|
||
}
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
<simpara><filename>test.php</filename></simpara>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
$d = new Dummy();
|
||
$d->printf("Привет, %s!\n", "мир");
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</informalexample>
|
||
</section>
|
||
</chapter>
|
||
<!-- Keep this comment at the end of the file
|
||
Local variables:
|
||
mode: sgml
|
||
sgml-omittag:t
|
||
sgml-shorttag:t
|
||
sgml-minimize-attributes:nil
|
||
sgml-always-quote-attributes:t
|
||
sgml-indent-step:1
|
||
sgml-indent-data:t
|
||
indent-tabs-mode:nil
|
||
sgml-parent-document:nil
|
||
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
||
sgml-exposed-tags:nil
|
||
sgml-local-catalogs:nil
|
||
sgml-local-ecat-files:nil
|
||
End:
|
||
vim600: syn=xml fen fdm=syntax fdl=2 si
|
||
vim: et tw=78 syn=sgml
|
||
vi: ts=1 sw=1
|
||
-->
|