Introduction
This book is not intended to be an exhaustive coverage of internal details of PHP, but rather a quick and easy reference for developers that are learning, exploring or even contributing to PHP itself.
For a more comprehensive and detailed write of how PHP works internally, the best available resource (besides the PHP source code itself), is The PHP Internals Book.
Copyright (c) 2022 - Flavio Heleno
Acknowledgements
This work is heavily inspired by the following amazing content (in alphabetical order):
- Julien Pauli's blog
- Nikita Popov's blog
- phpinternals.net
- phpinternalsbook/PHP-Internals-Book
- ThomasWeinert/php-extension-sample
- Writing PHP Extensions
Copyright (c) 2022 - Flavio Heleno
Structure
Before starting, it's important to understand the directory structure and the files that are used to build an extension and the core itself.
Copyright (c) 2022 - Flavio Heleno
Extension
The directory structure for an extension is simple enough, given the command:
php ext_skel.php \
--ext Hdi \
--onlyunix
The structure below will be created:
<hdi>
├─ tests/ # Code tests directory
│ ├─ 001.phpt # Test skeleton
│ ├─ 002.phpt # Test skeleton
│ └─ 003.phpt # Test skeleton
├─ .gitignore # Patterns to be ignored by git
├─ config.m4 # Code that will be imported by the configure script during build
├─ hdi.c # Actual extension code
├─ hdi.stub.php # Stub file that will be used to generate the "hdi_arginfo.h" file
├─ hdi_arginfo.h # Argument information header
└─ php_hdi.h # Extension registration header (module entry)
From Zend's chapter "2. Generating a PHP Extension Skeleton":
- config.m4 is an extension configuration script used by "phpize" or "buildconf" to add extension configuration options into the "configure" command.
- php_hdi.h is a C header file that contains our common extension definitions. It's not necessary for simple extensions with a single-source C file, but it's useful in case the implementation is spread among few files.
- hdi.c is the main extension implementation source. It defines all the structures that allow to plug the extension into PHP and make all their internal functions, classes and constants to be available.
hdi.stub.php
This stub file will be parsed by gen_stub.php and as
result, the hdi_arginfo.h
is generated/updated.
Note that not all PHP constructs are supported by gen_stub.php, in each chapter additional instructions for what to include and what not to include in the stub file will be outlined.
hdi_arginfo.h
This header file is automatically generated/updated by the build process and should not be manually editted.
Copyright (c) 2022 - Flavio Heleno
Constants
This chapter covers how to define a global constant.
Code sample for this chapter, with additional examples, is available here.
Userland PHP Code Snippet
The code below shows how a constant would be declared in userland PHP code.
const HELLO = 'World!';
Internal PHP Code
The following sections show all required code to declare and implement a constant internally in PHP.
PHP Stub (hdi.stub.php
)
The stub file should not declare global constants as the stub generator script (gen_stub.php) cannot handle it at this moment.
Argument Information (hdi_arginfo.h
)
Constants are not referred to nor registered in the argument information file, so it can be skipped.
Implementation (hdi.c
)
The implementation below is very simple and straight forward:
- declaration (
REGISTER_MAIN_STRING_CONSTANT
)- name string (
"HELLO"
) - value string (
"World!"
) - flags (
CONST_CS
andCONST_PERSISTENT
)
- name string (
REGISTER_MAIN_STRING_CONSTANT("HELLO", "World!", CONST_CS | CONST_PERSISTENT);
Macros and Functions:
References:
Note that constants must be registered during the MINIT() stage.
Copyright (c) 2022 - Flavio Heleno
Classes
Userland PHP Code Snippet
The code below shows how a class would be declared in userland PHP code.
class Hdi {
}
Internal PHP Code
The following sections show all required code to declare a class internally in PHP.
PHP Stub (hdi.stub.php
)
The stub file is used to declare the class signature:
- namespace (
global
) - name (
Hdi
)
class Hdi {}
Argument Information (hdi_arginfo.h
)
This file is generated during compilation, based on the stub file contents.
Header File (hdi.h
)
In this file, a pointer to a zend_class_entry
must be declared for the defined class:
extern zend_class_entry *zceHdi;
Implementation (hdi.c
)
The implementation below is elaborated and each part has its own purpose, as shown below:
Copyright (c) 2022 - Flavio Heleno
Class Constants
This chapter covers how to define a class constant.
Note that more in dept details about constants are covered in the constants chapter.
Code sample for this chapter, with additional examples, is available here.
Userland PHP Code Snippet
The code below shows how a class constant would be declared in userland PHP code.
class Hdi {
public const HELLO = 'World!';
}
Internal PHP Code
The following sections show all required code to declare and implement a class constant internally in PHP.
PHP Stub (hdi.stub.php
)
The stub file should not declare class constants as the stub generator script (gen_stub.php) cannot handle it at this moment.
Argument Information (hdi_arginfo.h
)
Class constants are not referred to nor registered in the argument information file, so it can be skipped.
Implementation (hdi.c
)
The implementation below is a little bit more elaborate than what is seen in global scope constant, but it can be broken into:
- name definition (
zend_string_init
)- name string (
"HELLO"
) - name size (
sizeof
) - persistent string (
0
ie. not persistent1)
- name string (
- value definition (
ZVAL_NEW_STR
andzend_string_init
)- value string (
"World!"
) - value size (
sizeof
) - persistent string (depends if
classEntry->type
is aZEND_INTERNAL_CLASS
or not)
- value string (
- declaration (
zend_declare_class_constant_ex
)- class entry (
classEntry
) - visibility (
ZEND_ACC_PRIVATE
)
- class entry (
zend_string *name = zend_string_init(
"HELLO",
sizeof("HELLO") - 1,
0
);
zval value;
ZVAL_NEW_STR(
&value,
zend_string_init(
"World!",
sizeof("World!") - 1,
classEntry->type & ZEND_INTERNAL_CLASS
)
);
zend_declare_class_constant_ex(
classEntry,
name,
&value,
ZEND_ACC_PRIVATE,
NULL
);
zend_string_release(name);
Alternatively, for public class constants, it can be simplified as:
zend_declare_class_constant_string(
classEntry,
"HELLO",
sizeof("HELLO") - 1,
"World!"
);
Macros and Functions:
- zend_string_init
- ZVAL_NEW_STR
- zend_declare_class_constant_ex
- zend_string_release
- zend_declare_class_constant_string
Visibility options:
Notes:
More about persistent objects in the Zend Memory Manager chapter of the PHP Internals Book.
Copyright (c) 2022 - Flavio Heleno
Class Methods
This chapter covers how to define a class method.
Note that details such as method arguments and method return values are covered in the functions chapter.
Code sample for this chapter, with additional examples, is available here.
Userland PHP Code Snippet
The code below shows how a class method would be declared and implemented in userland PHP code.
class Hdi {
public function sayHello(): string {
return 'Hello World!';
}
}
Internal PHP Code
The following sections show all required code to declare and implement a class method internally in PHP.
PHP Stub (hdi.stub.php
)
The stub file is used to declare the method signature:
- visibility (
public
) - name (
sayHello
) - argument list (
()
) - return type (
string
)
class Hdi {
public function sayHello(): string {}
}
Argument Information (hdi_arginfo.h
)
This file is generated during compilation, based on the stub file contents.
The argument information header is the standard/default file where all class methods details are defined:
- declaration (
ZEND_METHOD
)- class name (
Hdi
) - method name (
sayHello
)
- class name (
- class method list (
class_Hdi_methods[]
variable)- entry registration (
ZEND_ME
)- class name (
Hdi
) - method name (
sayHello
) - argument information (
arginfo_class_Hdi_sayHello
) - visibility (
ZEND_ACC_PUBLIC
)
- class name (
- entry registration (
- argument declaration (
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX
)- argument information name (
arginfo_class_Hdi_sayHello
) - return reference (
0
ie. return by value) - required arguments (
0
ie. no required arguments) - return type (
IS_STRING
) - allow null (
0
ie. not allowed)
- argument information name (
/* This is a generated file, edit the .stub.php file instead. */
/* NOTE: Only relevant code is shown below! */
ZEND_METHOD(Hdi, sayHello);
static const zend_function_entry class_Hdi_methods[] = {
ZEND_ME(Hdi, sayHello, arginfo_class_Hdi_sayHello, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Hdi_sayHello, 0, 0, IS_STRING, 0)
ZEND_END_ARG_INFO()
Macros and Functions:
Visibility options:
Type options:
- IS_LONG
- IS_DOUBLE
- IS_STRING
- IS_ARRAY
- IS_OBJECT
- IS_RESOURCE
- IS_REFERENCE
- IS_CALLABLE
- IS_ITERABLE
- IS_VOID
- IS_STATIC
- IS_MIXED
- IS_NEVER
- _IS_BOOL
Implementation (hdi.c
)
The implementation below is quite simple and straight forward:
- declaration (
PHP_METHOD
)- class name (
Hdi
) - method name (
sayHello
)
- class name (
- parameter parsing (
ZEND_PARSE_PARAMETERS_NONE
) - return (
RETURN_STR
)- value string (
"Hello World!"
)
- value string (
/* {{{ Hdi: function sayHello(): string */
PHP_METHOD(Hdi, sayHello) {
ZEND_PARSE_PARAMETERS_NONE();
RETURN_STR("Hello World!");
}
/* }}} */
Macros and Functions:
Copyright (c) 2022 - Flavio Heleno
Class Properties
This chapter covers how to define a class property.
Code sample for this chapter, with additional examples, is available here.
Userland PHP Code Snippet
The code below shows how a class property would be declared in userland PHP code.
class Hdi {
public int $value;
}
Internal PHP Code
The following sections show all required code to declare and implement a class property internally in PHP.
PHP Stub (hdi.stub.php
)
The stub file may or may not declare class properties, but the stub generator script (gen_stub.php) will not produce any output related to them as they are implementation specific.
Argument Information (hdi_arginfo.h
)
Class properties are not referred to nor registered in the argument information file, so it can be skipped.
Implementation (hdi.c
)
zend_string *propName = zend_string_init("value", sizeof("value") - 1, false);
zval propDefaultValue;
/* default property value (undefined) */
ZVAL_UNDEF(&propDefaultValue);
zend_declare_typed_property(
classEntry,
propName,
&defaultValue,
ZEND_ACC_PRIVATE,
NULL,
(zend_type)ZEND_TYPE_INIT_MASK(MAY_BE_LONG)
);
zend_string_release(propName);
Macros and Functions:
Visibility options:
Type options:
- MAY_BE_NULL
- MAY_BE_BOOL
- MAY_BE_LONG
- MAY_BE_DOUBLE
- MAY_BE_STRING
- MAY_BE_ARRAY
- MAY_BE_OBJECT
- MAY_BE_RESOURCE
- MAY_BE_ANY
- MAY_BE_REF
Manipulating Property Values
Update/Set a Property
Updating a property value is straight forward:
- update function (
zend_update_property_long
)- class entry (
classEntry
) - object reference (
Z_OBJ_P(ZEND_THIS)
ie.$this
) - property name string (
"value"
) - property name size (
sizeof
) - value (
42
)
- class entry (
zend_update_property_long(
classEntry,
Z_OBJ_P(ZEND_THIS),
"value",
sizeof("value") - 1,
42
);
Note that for static properties,
zend_update_property_long
must be replaced by its static version (zend_update_static_property_long
), that does not have an object argument.
Macros and Functions:
Read a Property
Reading a property value requires a bit more leg work:
- read function (
zend_read_property
)- class entry (
classEntry
) - object reference (
Z_OBJ_P(ZEND_THIS)
ie.$this
) - property name string (
"value"
) - property name size (
sizeof
) - ???
- ???
- class entry (
zval rv;
zval *value = zend_read_property(
classEntry,
Z_OBJ_P(ZEND_THIS),
"value",
sizeof("value") - 1,
true,
&rv
);
Note that for static properties,
zend_read_property
must be replaced by its static version (zend_read_static_property
), that does not have an object argument.
Macros and Functions:
Copyright (c) 2022 - Flavio Heleno