Kwert-soft.ru

IT Софт для ПК
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Php p skulle gerne loan

Изучаем PHP изнутри. Zval

Эта статья базируется на главе Zvals книги PHP Internals Book, переводом которой на русский язык я сейчас занимаюсь [ 1 ]. Книга ориентирована в первую очередь на C-программистов, желающих писать свои расширения для PHP, но, я уверен, что она окажется полезной и для PHP-разработчиков, так как описывает внутреннюю логику работы интерпретатора. В статье я оставил только базовую теорию, которая должна быть понятна всем разработчикам (даже не знакомым с PHP или C). За более полным изложением материала обратитесь к книге.

Задачка для привлечения внимания. Каким будет результат выполнения следующего кода?

Базовая структура

Базовой структурой данных в PHP является zval (сокращение от «Zend value»). Каждый zval хранит в себе несколько полей, два из которых — значение и тип этого значения. Это необходимо потому что PHP — это язык с динамической типизацией и поэтому тип переменных известен только во время выполнения программы, а не во время компиляции. Кроме того, тип переменной может быть изменен в течении жизни zval, то есть zval ранее хранимый как целое число позднее может содержать строку.

Тип переменной хранится как целочисленная метка (type tag, unsigned char). Метка может принимать одно из 8 значений, которое соответствует 8 типам данных доступных в PHP. Эти значения должны присваиваться с использованием констант вида IS_TYPE . Например, IS_NULL соответствует типу данных null, а IS_STRING — строке.

zvalue_value

Фактическое значение переменной хранится в типе данных union («объединение», в дальнейшем я буду использовать термины union или юнион), который определен следующим образом:

Небольшое пояснение для тех кто не знаком с концепцией union-ов. Union определяет несколько членов-данных различных типов, но в каждый момент времени может использоваться только одно значение из определенных в юнионе. Например, если члену данных value.lval было присвоено значение, то для доступа к данным вы можете использовать только value.lval , доступ к другим членам данных недопустим и может приводить к непредсказуемому поведению программы. Причина этого в том, что юнионы хранят данные всех своих членов в одной области памяти и интерпретируют значение по разному исходя из имени, к которому вы обращаетесь. Размер памяти, выделяемой для юниона, соответствует размеру самого большого его члена-данных.

При работе с zval-ами используется специальная метка (type tag), которая позволяет определить какой тип данных хранится в юнионе в данный момент. Прежде чем обратиться к API давайте посмотрим какие типы данных поддерживаются в PHP и как они хранятся.

Простейший тип данных — IS_NULL : он не должен хранить какое-либо значение, так как это просто null.

Для хранения чисел PHP представляет 2 типа: IS_LONG и IS_DOUBLE , которые используют члены long lval и double dval соответственно. Первый используется для хранения целых чисел, второй — для чисел с плавающей точкой.

Есть несеколько вещей, которые вам следует знать о типе данных long. Во-первых, это signed integer, то есть он может содержать положительные и отрицательные значения, но этот тип данных не подходит для побитовых операций. Во-вторых, long имеет разные размеры на разных платформах: на 32-битных системах он имеет размер 32 бита или 4 байта, но на 64-битных системах он может иметь размер как 4, так и 8 байт. В Unix-системах он обычно имеет размер в 8 байт, в то время как в 64-битных версиях Windows использует только 4 байта.

По этой причине вы не должны полагаться на конкретное значение типа long. Минимальное и максимальное значения, которые могут быть сохранены в типе данных long доступны в константах LONG_MIN и LONG_MAX и размер этого типа может быть определен с использованием макро SIZEOF_LONG (в отличии от sizeof(long) этот макро может быть использован и в #if директивах).

Тип данных double предназначен для хранения чисел с плавающей точкой и, обычно, следуя спецификации IEEE-754, он имеет размер в 8 байт. Детали этого формата не будут обсуждаться здесь, но вам как минимум следует знать, что этот тип имеет ограниченную точность и часто хранит не точно то значение, на которое вы рассчитываете.

Булевы переменные используют флаг IS_BOOL и хранятся в поле long val как значения 0 (false) и 1 (true). Так как этот тип использут только 2 значения, то, теоретически, достаточно было использовать тип меньшего размера (например zend_bool), но так как zvalue_value — это юнион и под него и так выделен объем памяти соответствующий самому большому члену данных, то применение более компактной переменной для булевых значений не приведет к экономии памяти. Поэтому lval повторно использован в этом случае.

Строки ( IS_STRING ) хранятся в структуре struct str; , то есть строка хранится как указатель на строку char * и целочисленная длина строки int . Строки в PHP должны явно хранить свою длину для того чтобы иметь возможность содержать NUL байты () и быть бинарно безопасными (binary safe). Но несмотря на это, строки используемые в PHP все равно заканчиваются нулевым байтом (NUL-terminated), чтобы обеспечить совместимость с библиотечными функциями, которые не принимают аргумент с длиной строки, а ожидают найти нулевой байт в конце строки. Конечно, в таких случаях строки больше не могут быть бинарно безопасными и будут обрезаны до первого вхождения нулевого байта. Например, много функций связанных с файловой системой и большинство строковых функций из libc ведут себя подобным образом.

Читать еще:  Лучшая бесплатная защита от вирусов

Длина строки измеряется в байтах (не числом Unicode-символов) и не должно включать нулевой байт, то есть длина строки foo равна 3, несмотря на то, что для её хранения используется 4 байта. Если вы оперделяете длину строки с использованием sizeof вам нужно вычитать единицу: strlen(«foo») == sizeof(«foo») — 1 .

Очень важно понимать: длина строки хранится в типе int, а не в long или каком-то другом похожем типе. Это исторический артефакт, который ограничивает длину строки 2147483647 байтами (2 гигабайта). Строки большего размера будут причиной переполнения (что сделает их длину отрицательной).

Оставшиеся три типа будут упомянуты лишь поверхностно.

Массивы используют метку IS_ARRAY и хранятся в члене-данных HashTable *ht . Как работает структура данных HashTable рассмотрено в другой статье.

Объекты ( IS_OBJECT ) исползуют член-данных zend_object_value obj , который состоит из «object handle» (целочисленный ID, используемый для поиска реальных данных) и набора «object handlers», которые определяют поведение объекта. Система классов и объектов в PHP будет описана в главе «Классы и объекты».

Ресурсы ( IS_RESOURCE ) похожи на объекты, так как они также хранят уникальный ID, используемый для поиска значения. Этот ID хранится в члене long lval. Ресурсы будут описаны в соответствующей главе, которая пока не написана.

Подведем промежуточный итог, ниже представлена таблица с перечислением всех доступных меток типов и соответствующее им хранилище значений:

Type tagStorage location
IS_NULLnone
IS_BOOLlong lval
IS_LONGlong lval
IS_DOUBLEdouble dval
IS_STRINGstruct < char *val; int len; >str
IS_ARRAYHashTable *ht
IS_OBJECTzend_object_value obj
IS_RESOURCElong lval

Давайте теперь посмотрим как выглядит структура данных zval:

Как уже упоминалось, zval содержит члены для хренения значения и его типа. Значение хранится в юнионе zvalue_value , который описан выше. Тип хранится в zend_uchar type . Кроме того эта структура содержит 2 дополнительных свойства, имена которых заканчиваются на __gc , которые используются механизмом сборки мусора. Подробнее эти свойства рассмотрены в следующем разделе.

Управление памятью

Структура данных zval играет 2 роли. Во-первых, как было описано в предыдущем разделе, она хранит данные и их тип. Во-вторых (это будет рассмотрено в текущем разделе) используется для эффективного управления значениями в памяти.

В этом разделе мы рассмотрим концепции подсчета ссылок и копирования-при-записи (copy-on-write).

Семантика знечений и ссылок

В PHP все значения всегда имеют семантику значений (value-semantics), только если вы явно не запросили использование ссылок. Это значит, что и при передаче значения в функцию, и при выполнении операции присваивания вы будете работать с 2 разными копиями значения. Пара примеров поможет убедиться в этом:

Пример выше очень прост и очевиден, но важно понимать, что это основное правило, применяемое везде. Оно также применимо и к объектам:

Часто можно услышать, что в PHP 5 объекты автоматически передаются по ссылке, но пример выше показывает, что это не так. Функция, в которую передается значение не может изменить значение переданной переменной, только функция, в которую передается ссылка, может сделать это.

Это так и есть, хотя объекты действительно ведут себя так, будто они переданы по ссылке. Вы не можете присвоить переменной другое значение, но вы можете менять свойства объекта. Это возможно потому, что значением объекта является ID, который используется для поиска «реальных данных» объекта. Семантика передачи по значению не даст вам изменить этот ID на другой или поменять тип переменной, но она не помешает вам изменить «реальные данные» объекта.

Немного изменим пример выше:

То же самое можно сказать и про ресурсы, так как они тоже хранят только ID, который может быть использован для поиска данных. Итак, еще раз, семантика передачи по значению не дает вам изменить ID или тип zval-а, но не мешает вам сменить данные ресурса (например сдвинуть позицию в файле).

Подсчет ссылок и копирование-при-записи

Если вы немного поразмышляете о написанном выше, то вы придете к заключению, что PHP должен выполнять огромное число операций копирования. Каждый раз передавая переменную в функцию её значение должно быть скопировано. Это может не быть проблемой для данных типа integer или double, но представьте, что вы передаете в функцию массив, содержащий десять миллионов значений. Копирование миллионов значений при каждом вызове функции — это недопустимо медленно.

Для того чтобы избежать этого в PHP используется парадигма копирования-при-записи (copy-on-write). Zval может совместно использоваться множеством переменных/функций/и т.д., но только до тех пор пока данные zval-а используются для чтения. Как только кто-то захочет изменить данные zval-а, он будет скоприрован прежде чем изменения будут применены.

Так как один zval может быть использован в нескольких местах, PHP должен иметь возможность определить момент, кода zval больше никем не используется и удалить его (освободить занимаемую им память). PHP делает это простым подсчетом ссылок. Учтите, что «ссылка» здесь это не ссылка в терминах PHP (та, что задается при помощи & ), а просто показатель, говорящий что кто-то (переменная, функция, и т.д.) использует этот zval. Число таких ссылок называется refcount и оно хранится в члене-данных refcount__gc zval-а.

Читать еще:  Php переместить файл в другую папку

Чтобы понять как это работает давайте рассмотрим пример:

Логика здесь простая: когда ссылка добавляется, значение refcount увеличивается на единицу, когда ссылка удаляется — refcount уменьшается. Когда значение refcount достигает 0 — zval удаляется.

Правда, этот метод не будет работать в случае циклических ссылок:

После того как код приведенный выше будет запущен мы получим ситуацию, в которой у нас будет два zval-а недоступных ни через одну переменную, но все еще существующих в памяти, так как они ссылаются друг на друга. Это классический пример проблемы с подсчетом ссылок.

Для решения этой проблемы в PHP реализован еще один механизм сборки мусора — циклический сборщик. Мы можем его сейчас проигнорировать так как циклический сборщик (в отличии от механизма подсчета ссылок) прозрачен для разработчиков расширений PHP. Если вам интересна эта тема, то обратитесь к документации PHP, в которой описан этот алгоритм.

Есть еще одна особенность PHP-ссылок (тех, что определяются как &$var , а не тех, что были рассмотрены выше), которая должна быть рассмотрена. Для того чтобы обозначить, что zval используется как PHP-ссылка используется флаг is_ref__gc в структуре zval.

Если is_ref=1 это является сигналом к тому, что zval не должен быть скопирован перед модификацией, вместо этого должно быть изменено значение zval-а:

В примере выше zval переменной $a перед созданием ссылки имеет refcount=1 . Теперь рассмотрим похожий пример с числом ссылок большим чем 1:

Как вы видите, при создании ссылки на zval c is_ref=0 и refcount>1 требует создания копии. Аналогично, при использовании zval с is_ref=1 и refcount>1 в контексте с передачей по значению потребуется операция копирования. По этой причине использование PHP-ссылок обычно замедляет код. Почти все функции в PHP используют семантику передачи по значению, поэтому они создают копию при получении zval со значением is_ref=1 .

Заключение

В этой статье я привел выжимку главы Zvals книги PHP Internals Book. Я постарался оставить только тот материал, который будет полезен PHP-разработчикам и вырезал много текста, связанного с разработкой расширений (иначе объем статьи вырос бы раза в 3). Если вам интересно глубже изучить вопрос разработки расширений для PHP вы можете обратиться к книге или моему переводу. На данный момент переведена только глава Zvals, но я продолжаю работу. В ближайшее время займусь интереснейшими главами про хештаблицы и классы.

Php p skulle gerne loan

PHP Quick Start

This guide gets you started with gRPC in PHP with a simple working example.

Prerequisites

  • PHP 5.5 or higher, 7.0 or higher
  • PECL
  • Composer
  • PHPUnit (optional)

Install PHP and PECL on Ubuntu/Debian:

Install PHP and PECL on CentOS/RHEL 7:

Install PHP and PECL on Mac:

Install Composer (Linux or Mac):

Install PHPUnit (Linux or Mac):

Install the gRPC PHP extension

There are two ways to install gRPC PHP extension:

Using PECL

or specific version

Install on Windows

You can download the pre-compiled gRPC extension from the PECL website

Build from Source with gRPC C core library

Clone this repository at given release tag

Build and install the gRPC C core library
Build and install gRPC PHP extension

Compile the gRPC PHP extension

This will compile and install the gRPC PHP extension into the standard PHP extension directory. You should be able to run the unit tests with the PHP extension installed.

Update php.ini

After installing the gRPC extension, make sure you add this line to your php.ini file, (e.g. /etc/php5/cli/php.ini , /etc/php5/apache2/php.ini , or /usr/local/etc/php/5.6/php.ini ), depending on where your PHP installation is.

Add the gRPC PHP library as a Composer dependency

You need to add this to your project’s composer.json file.

To run tests with generated stub code from .proto files, you will also need the composer and protoc binaries. You can find out how to get these below.

Install other prerequisites for both Mac OS X and Linux

  • protoc: protobuf compiler
  • protobuf.so: protobuf runtime library
  • grpc_php_plugin: Generates PHP gRPC service interface out of Protobuf IDL

Install Protobuf compiler

If you don’t have it already, you need to install the protobuf compiler protoc , version 3.4.0+ (the newer the better) for the current gRPC version. If you installed already, make sure the protobuf version is compatible with the grpc version you installed. If you build grpc.so from source, you can check the version of grpc inside package.xml file.

The compatibility between the grpc and protobuf version is listed as table below:

grpcprotobuf
v1.0.03.0.0(GA)
v1.0.13.0.2
v1.1.03.1.0
v1.2.03.2.0
v1.2.03.2.0
v1.3.43.3.0
v1.3.53.2.0
v1.4.03.3.0
v1.6.03.4.0

If protoc hasn’t been installed, you can download the protoc binaries from the protocol buffers GitHub repository. Then unzip this file and Update the environment variable PATH to include the path to the protoc binary file./protobuf/releases). Then unzip this file and Update the environment variable PATH to include the path to the protoc binary file.

If you really must compile protoc from source, you can run the following commands, but this is risky because there is no easy way to uninstall / upgrade to a newer release.

Protobuf Runtime library

There are two protobuf runtime libraries to choose from. They are identical in terms of APIs offered. The C implementation provides better performance, while the native implementation is easier to install. Make sure the installed protobuf version works with grpc version.

C implementation (for better performance)

or specific version

After protobuf extension is installed, Update php.ini by adding this line to your php.ini file, (e.g. /etc/php5/cli/php.ini , /etc/php5/apache2/php.ini , or /usr/local/etc/php/5.6/php.ini ), depending on where your PHP installation is.

PHP implementation (for easier installation)

Add this to your composer.json file:

PHP Protoc Plugin

You need the gRPC PHP protoc plugin to generate the client stub classes. It can generate server and client code from .proto service definitions.

It should already been compiled when you run make from the root directory of this repo. The plugin can be found in the bins/opt directory. We are planning to provide a better way to download and install the plugin in the future.

You can also just build the gRPC PHP protoc plugin by running:

Plugin may use the new feature of the new protobuf version, thus please also make sure that the protobuf version installed is compatible with the grpc version you build this plugin.

Download the example

You’ll need a local copy of the example code to work through this quick start. Download the example code from our GitHub repository (the following command clones the entire repository, but you just need the examples for this quick start and other tutorials):

Note that currently you can only create clients in PHP for gRPC services — you can find out how to create gRPC servers in our other tutorials, e.g. Node.js.

Run a gRPC application

From the examples/node directory:

From another terminal, from the examples/php directory, run the client:

Congratulations! You’ve just run a client-server application with gRPC.

Update a gRPC service

Now let’s look at how to update the application with an extra method on the server for the client to call. Our gRPC service is defined using protocol buffers; you can find out lots more about how to define a service in a .proto file in gRPC Basics: PHP. For now all you need to know is that both the server and the client “stub” have a SayHello RPC method that takes a HelloRequest parameter from the client and returns a HelloResponse from the server, and that this method is defined like this:

Let’s update this so that the Greeter service has two methods. Edit examples/protos/helloworld.proto and update it with a new SayHelloAgain method, with the same request and response types:

Remember to save the file!

Generate gRPC code

Next we need to update the gRPC code used by our application to use the new service definition. From the grpc root directory:

or running the helper script under the grpc/example/php directory if you build grpc-php-plugin by source:

This regenerates the protobuf files, which contain our generated client classes, as well as classes for populating, serializing, and retrieving our request and response types.

Update and run the application

We now have new generated client code, but we still need to implement and call the new method in the human-written parts of our example application.

Update the server

In the same directory, open greeter_server.js . Implement the new method like this:

Update the client

In the same directory, open greeter_client.php . Call the new method like this:

Just like we did before, from the examples/node/dynamic_codegen directory:

From another terminal, from the examples/php directory, run the client:

What’s next

  • Read a full explanation of how gRPC works in What is gRPC? and gRPC Concepts.
  • Work through a more detailed tutorial in gRPC Basics: PHP.
  • Explore the gRPC PHP core API in its reference documentation.

© gRPC Authors 2020 | Documentation Distributed under CC-BY-4.0

© 2020 The Linux Foundation. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page.

Ссылка на основную публикацию
Adblock
detector