PHP
PHP is a server-side scripting language.
Installation
Refer to the Dockerfiles at https://git.steamr.com/docker/php-fpm if you wish to install PHP-FPM manually.
Interpreter
The PHP interpreter consists of two parts: The PHP Core and the Zend Engine.
The PHP core handles communication with bindings, SAPIs, file and network IO, and handling of the many PHP extensions.
The Zend Engine is responsible for interpreting the PHP code into bytecode. The interpretation process involves splitting the code into tokens using a tokenizer (initiated by PHP's core function zend_compile_file()
) and converting these tokens into bytecode. zend_compile_string()
can also be used to compiles a string and is used for functions such as eval()
.
The PHP bytecode is executed by the PHP's virtual machine. The VM has a virtual CPU with its own set of instructions. Execution is initiated by passing an array of opcodes to zend_execute()
.
Bytecode
The register-based bytecode of PHP consists of opcodes, constants, variables, and meta information. PHP has around 200 different opcodes that cover all existing language constructs. (see https://www.php.net/manual/en/internals2.opcodes.php).
Each opcode conforms to the zend_op
data structure and has:
- an opcode number (
znode_uchar
) used to find the corresponding opcode handler in a lookup table. - two operand parameters (value as
znode_op
, type asznode_uchar
) - a result operand to store return values (
znode_op
, type asznode_uchar
)
5 types:
- IS_UNUSED: Operand not used
- IS_CONST: Constants are literals in code
- IS_TMPVAR: Temporary variable, as result of
$foo+$bar
for example.~0
, where 0 would be the first, 1 the second, an so on. - IS_VAR: Non-simple variables or variables requiring a lookup.
$n
. - IS_CV: Compiled variable to save PHP from looking up variables in hash table.
!n
, where n is the offset to the compiled-variable array.
Index to retrieve the handler address in the lookup table is:
See Also:
Encoders
- ionCube
- Zend Guard
- SourceGuardian
ionCube header looks like:
<?php //0xxxx
*load extension / print message code*
binary encoded data using custom Base64
The file is checked using Adler32 and binary contents are encoded using a custom Base64 format.
The ionCube loader hooks zend_compile_file()
and tests for <?php //
at the beginning of an executed PHP file. The hexadecimal value immediately following this specifies the size of the fallback code which the loader skips. The binary encoded data is then loaded and parsed by the ionCube extension.
Stuff
Error Reporting
To always show all errors and warnings:
error_reporting(E_ALL);
ini_set('display_errors', 1);
In more detail:
// Turn off all error reporting
error_reporting(0);
// Report simple running errors
error_reporting(E_ERROR | E_WARNING | E_PARSE);
// Reporting E_NOTICE can be good too (to report uninitialized
// variables or catch variable name misspellings ...)
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
// Report all errors except E_NOTICE
// This is the default value set in php.ini
error_reporting(E_ALL ^ E_NOTICE);
// Report all PHP errors (see changelog)
error_reporting(E_ALL);
// Report all PHP errors
error_reporting(-1);
// Same as error_reporting(E_ALL);
ini_set('error_reporting', E_ALL);
UTF-8 to hex
There is basically NO good way.
- http://www.tokyomuslim.com/2008/01/php-convert-utf-8-to-hex-codepoint-values-unicode-hexidecimal/
- http://hsivonen.iki.fi/php-utf8/
Troubleshooting
HTTP POST Array Empty
I had an issue where a PHP POST array was empty when a POST request was made. It turns out that PHP now needs the Content-Type
header set for it to be populated regardless of the HTTP method being used.
In detail, when the following request was made, $_POST
was empty:
POST /print HTTP/1.1
Connection: Keep-Alive
User-Agent: WebSlipPrinting
Content-Length: 56
Host: 172.20.1.151
template_data=Hello There!&Username=test&Password=123456
The data though was still sent since $_SERVER['CONTENT_LENGTH']
was 56 as in the original request. The data was also present in the php://input
stream. ie:
var_dump(file_get_contents("php://input"));
# Returned:
# string(56) "template_data=Hello There!&Username=test&Password=123456"
Adding the Content-Type
value to the header fixes the issue, with the final request as:
POST /print HTTP/1.1
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded
User-Agent: WebSlipPrinting
Content-Length: 56
Host: 172.20.1.151
template_data=Hello There!&Username=test&Password=123456
This may have something to do with the enable_post_data_reading
option in PHP. From the config file:
647 ; Whether PHP will read the POST data.
648 ; This option is enabled by default.
649 ; Most likely, you won't want to disable this option globally. It causes $_POST
650 ; and $_FILES to always be empty; the only way you will be able to read the
651 ; POST data will be through the php://input stream wrapper. This can be useful
652 ; to proxy requests or to process the POST data in a memory efficient fashion.
653 ; http://php.net/enable-post-data-reading
654 ;enable_post_data_reading = Off
PDOException : could not find driver
You need to have the pdo driver installed. In my case, I was trying to use the pdo-mysql connector which wasn't compiled with PHP. To fix this, install the PDO driver or compile it manually with:
--with-pdo-<driver>
In my case, I needed mysql:
--with-pdo-mysql
mail()
issues
If mail()
returns sh: -t: command not found
when attempting to run the mail()
function, you probably did not set the sendmail_path
variable in your php.ini
.
Change:
; sendmail_path =
To:
sendmail_path = /usr/sbin/sendmail -t -i
If you get Recipient names must be specified
when calling mail()
, you need to set the -t
flag in sendmail_path
in php.ini
.