2018-05-27 02:23:11 +08:00
|
|
|
#!/usr/bin/env php
|
|
|
|
<?php
|
2017-07-23 16:36:35 +08:00
|
|
|
/*
|
|
|
|
+----------------------------------------------------------------------+
|
2019-01-30 17:03:12 +08:00
|
|
|
| Copyright (c) The PHP Group |
|
2017-07-23 16:36:35 +08:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
|
|
| available through the world-wide-web at the following url: |
|
2021-05-06 18:16:35 +08:00
|
|
|
| https://www.php.net/license/3_01.txt |
|
2017-07-23 16:36:35 +08:00
|
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Authors: Kalle Sommer Nielsen <kalle@php.net> |
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* $Id$ */
|
|
|
|
|
|
|
|
function error($message) {
|
2020-02-03 20:41:31 +08:00
|
|
|
printf('Error: %s%s', $message, PHP_EOL);
|
|
|
|
exit;
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function print_help() {
|
2019-03-11 09:01:25 +08:00
|
|
|
if (PHP_OS_FAMILY != 'Windows') {
|
|
|
|
$file_prefix = './';
|
|
|
|
$make_prefix = '';
|
|
|
|
} else {
|
|
|
|
$file_prefix = '';
|
|
|
|
$make_prefix = 'n';
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2019-03-11 09:01:25 +08:00
|
|
|
echo <<<HELP
|
|
|
|
WHAT IT IS
|
|
|
|
|
|
|
|
It's a tool for automatically creating the basic framework for a PHP extension.
|
|
|
|
|
|
|
|
HOW TO USE IT
|
|
|
|
|
|
|
|
Very simple. First, change to the ext/ directory of the PHP sources. Then run
|
|
|
|
the following
|
|
|
|
|
|
|
|
php ext_skel.php --ext extension_name
|
|
|
|
|
|
|
|
and everything you need will be placed in directory ext/extension_name.
|
|
|
|
|
|
|
|
If you don't need to test the existence of any external header files,
|
|
|
|
libraries or functions in them, the extension is ready to be compiled in PHP.
|
|
|
|
To compile the extension run the following:
|
|
|
|
|
|
|
|
cd extension_name
|
|
|
|
phpize
|
|
|
|
{$file_prefix}configure
|
|
|
|
{$make_prefix}make
|
|
|
|
|
|
|
|
Don't forget to run tests once the compilation is done:
|
|
|
|
|
|
|
|
{$make_prefix}make test
|
|
|
|
|
|
|
|
Alternatively, to compile extension in the PHP:
|
|
|
|
|
|
|
|
cd /path/to/php-src
|
|
|
|
{$file_prefix}buildconf
|
|
|
|
{$file_prefix}configure --enable-extension_name
|
|
|
|
{$make_prefix}make
|
|
|
|
{$make_prefix}make test TESTS=ext/extension_name/tests
|
|
|
|
|
|
|
|
The definition of PHP_extension_NAME_VERSION will be present in the
|
|
|
|
php_extension_name.h and injected into the zend_extension_entry definition.
|
|
|
|
This is required by the PECL website for the version string conformity checks
|
|
|
|
against package.xml
|
|
|
|
|
|
|
|
SOURCE AND HEADER FILE NAME
|
|
|
|
|
|
|
|
The ext_skel.php script generates 'extension_name.c' and 'php_extension_name.h'
|
|
|
|
as the main source and header files. Keep these names.
|
|
|
|
|
|
|
|
extension functions (User functions) must be named
|
|
|
|
|
|
|
|
extension_name_function()
|
|
|
|
|
|
|
|
When you need to expose extension functions to other extensions, expose
|
|
|
|
functions strictly needed by others. Exposed internal function must be named
|
|
|
|
|
|
|
|
php_extension_name_function()
|
|
|
|
|
2019-05-19 03:39:19 +08:00
|
|
|
See also CODING_STANDARDS.md.
|
2019-03-11 09:01:25 +08:00
|
|
|
|
|
|
|
OPTIONS
|
|
|
|
|
|
|
|
php ext_skel.php --ext <name> [--experimental] [--author <name>]
|
|
|
|
[--dir <path>] [--std] [--onlyunix]
|
|
|
|
[--onlywindows] [--help]
|
|
|
|
|
|
|
|
--ext <name> The name of the extension defined as <name>
|
|
|
|
--experimental Passed if this extension is experimental, this creates
|
|
|
|
the EXPERIMENTAL file in the root of the extension
|
|
|
|
--author <name> Your name, this is used if --std is passed and for the
|
|
|
|
CREDITS file
|
|
|
|
--dir <path> Path to the directory for where extension should be
|
|
|
|
created. Defaults to the directory of where this script
|
|
|
|
lives
|
|
|
|
--std If passed, the standard header used in extensions that
|
|
|
|
is included in the core, will be used
|
|
|
|
--onlyunix Only generate configure scripts for Unix
|
|
|
|
--onlywindows Only generate configure scripts for Windows
|
|
|
|
--help This help
|
|
|
|
|
|
|
|
HELP;
|
2020-02-03 20:41:31 +08:00
|
|
|
exit;
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function task($label, $callback) {
|
2020-02-03 20:41:31 +08:00
|
|
|
printf('%s... ', $label);
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
$callback();
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
printf('done%s', PHP_EOL);
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function print_success() {
|
2020-02-03 20:41:31 +08:00
|
|
|
global $options;
|
|
|
|
|
|
|
|
if (PHP_OS_FAMILY != 'Windows') {
|
|
|
|
$file_prefix = './';
|
|
|
|
$make_prefix = '';
|
|
|
|
} else {
|
|
|
|
$file_prefix = '';
|
|
|
|
$make_prefix = 'n';
|
|
|
|
}
|
|
|
|
|
|
|
|
printf('%1$sSuccess. The extension is now ready to be compiled. To do so, use the%s', PHP_EOL);
|
|
|
|
printf('following steps:%1$s%1$s', PHP_EOL);
|
2021-08-19 15:43:32 +08:00
|
|
|
printf('cd %s%s%s', $options['dir'], $options['ext'], PHP_EOL);
|
2020-02-03 20:41:31 +08:00
|
|
|
printf('phpize%s', PHP_EOL);
|
|
|
|
printf('%sconfigure%s', $file_prefix, PHP_EOL);
|
|
|
|
printf('%smake%2$s%2$s', $make_prefix, PHP_EOL);
|
|
|
|
printf('Don\'t forget to run tests once the compilation is done:%s', PHP_EOL);
|
|
|
|
printf('%smake test%2$s%2$s', $make_prefix, PHP_EOL);
|
|
|
|
printf('Thank you for using PHP!%s', PHP_EOL);
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function process_args($argv, $argc) {
|
2020-02-03 20:41:31 +08:00
|
|
|
$options = [
|
|
|
|
'unix' => true,
|
|
|
|
'windows' => true,
|
|
|
|
'ext' => '',
|
|
|
|
'dir' => __DIR__ . DIRECTORY_SEPARATOR,
|
|
|
|
'skel' => __DIR__ . DIRECTORY_SEPARATOR . 'skeleton' . DIRECTORY_SEPARATOR,
|
|
|
|
'author' => false,
|
|
|
|
'experimental' => false,
|
|
|
|
'std' => false
|
|
|
|
];
|
|
|
|
|
|
|
|
for($i = 1; $i < $argc; ++$i)
|
|
|
|
{
|
|
|
|
$val = $argv[$i];
|
|
|
|
|
|
|
|
if($val[0] != '-' || $val[1] != '-')
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch($opt = strtolower(substr($val, 2)))
|
|
|
|
{
|
|
|
|
case 'help': {
|
|
|
|
print_help();
|
|
|
|
}
|
|
|
|
case 'onlyunix': {
|
|
|
|
$options['windows'] = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'onlywindows': {
|
|
|
|
$options['unix'] = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'experimental': {
|
|
|
|
$options['experimental'] = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'std': {
|
|
|
|
$options['std'] = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'ext':
|
|
|
|
case 'dir':
|
|
|
|
case 'author': {
|
|
|
|
if (!isset($argv[$i + 1]) || ($argv[$i + 1][0] == '-' && $argv[$i + 1][1] == '-')) {
|
|
|
|
error('Argument "' . $val . '" expects a value, none passed');
|
|
|
|
} else if ($opt == 'dir' && empty($argv[$i + 1])) {
|
|
|
|
continue 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
$options[$opt] = ($opt == 'dir' ? realpath($argv[$i + 1]) . DIRECTORY_SEPARATOR : $argv[$i + 1]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: {
|
|
|
|
error('Unsupported argument "' . $val . '" passed');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (empty($options['ext'])) {
|
|
|
|
error('No extension name passed, use "--ext <name>"');
|
|
|
|
} else if (!$options['unix'] && !$options['windows']) {
|
|
|
|
error('Cannot pass both --onlyunix and --onlywindows');
|
|
|
|
} else if (!is_dir($options['skel'])) {
|
|
|
|
error('The skeleton directory was not found');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate extension name
|
|
|
|
if (!preg_match('/^[a-z][a-z0-9_]+$/i', $options['ext'])) {
|
|
|
|
error('Invalid extension name. Valid names start with a letter,'
|
|
|
|
.' followed by any number of letters, numbers, or underscores.'
|
|
|
|
.' Using only lower case letters is preferred.');
|
|
|
|
}
|
|
|
|
|
|
|
|
$options['ext'] = str_replace(['\\', '/'], '', strtolower($options['ext']));
|
|
|
|
|
|
|
|
return $options;
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function process_source_tags($file, $short_name) {
|
2020-02-03 20:41:31 +08:00
|
|
|
global $options;
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
$source = file_get_contents($file);
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
if ($source === false) {
|
|
|
|
error('Unable to open file for reading: ' . $short_name);
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
$source = str_replace('%EXTNAME%', $options['ext'], $source);
|
|
|
|
$source = str_replace('%EXTNAMECAPS%', strtoupper($options['ext']), $source);
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
if (strpos($short_name, '.c') !== false || strpos($short_name, '.h') !== false) {
|
|
|
|
static $header;
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
if (!$header) {
|
|
|
|
if ($options['std']) {
|
|
|
|
$author_len = strlen($options['author']);
|
|
|
|
$credits = $options['author'] . ($author_len && $author_len <= 60 ? str_repeat(' ', 60 - $author_len) : '');
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
$header = <<<"HEADER"
|
2017-07-23 16:36:35 +08:00
|
|
|
/*
|
|
|
|
+----------------------------------------------------------------------+
|
2019-02-04 05:47:49 +08:00
|
|
|
| Copyright (c) The PHP Group |
|
2017-07-23 16:36:35 +08:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
|
|
| available through the world-wide-web at the following url: |
|
2021-05-06 18:16:35 +08:00
|
|
|
| https://www.php.net/license/3_01.txt |
|
2017-07-23 16:36:35 +08:00
|
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Author: $credits |
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
*/
|
|
|
|
HEADER;
|
2020-02-03 20:41:31 +08:00
|
|
|
} else {
|
|
|
|
if ($options['author']) {
|
|
|
|
$header = sprintf('/* %s extension for PHP (c) %d %s */', $options['ext'], date('Y'), $options['author']);
|
|
|
|
} else {
|
|
|
|
$header = sprintf('/* %s extension for PHP */', $options['ext']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$source = str_replace('%HEADER%', $header, $source);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!file_put_contents($file, $source)) {
|
|
|
|
error('Unable to save contents to file: ' . $short_name);
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function copy_config_scripts() {
|
2020-02-03 20:41:31 +08:00
|
|
|
global $options;
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
$files = [];
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
if ($options['unix']) {
|
|
|
|
$files[] = 'config.m4';
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
if ($options['windows']) {
|
|
|
|
$files[] = 'config.w32';
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
$files[] = '.gitignore';
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
foreach($files as $config_script) {
|
|
|
|
$new_config_script = $options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $config_script;
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
if (!copy($options['skel'] . $config_script . '.in', $new_config_script)) {
|
|
|
|
error('Unable to copy config script: ' . $config_script);
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
process_source_tags($new_config_script, $config_script);
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function copy_sources() {
|
2020-02-03 20:41:31 +08:00
|
|
|
global $options;
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
$files = [
|
|
|
|
'skeleton.c' => $options['ext'] . '.c',
|
2020-02-03 20:41:45 +08:00
|
|
|
'skeleton.stub.php' => $options['ext'] . '.stub.php',
|
2020-03-23 09:45:34 +08:00
|
|
|
'php_skeleton.h' => 'php_' . $options['ext'] . '.h',
|
|
|
|
'skeleton_arginfo.h' => $options['ext'] . '_arginfo.h'
|
2020-02-03 20:41:31 +08:00
|
|
|
];
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
foreach ($files as $src_file => $dst_file) {
|
|
|
|
if (!copy($options['skel'] . $src_file, $options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $dst_file)) {
|
|
|
|
error('Unable to copy source file: ' . $src_file);
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
process_source_tags($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $dst_file, $dst_file);
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function copy_tests() {
|
2020-02-03 20:41:31 +08:00
|
|
|
global $options;
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
$test_files = glob($options['skel'] . 'tests/*', GLOB_MARK);
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
if (!$test_files) {
|
|
|
|
return;
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
foreach ($test_files as $test) {
|
|
|
|
if (is_dir($test)) {
|
|
|
|
continue;
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
$new_test = str_replace([$options['skel'], '/'], ['', DIRECTORY_SEPARATOR], $test);
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
if (!copy($test, $options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $new_test)) {
|
|
|
|
error('Unable to copy file: ' . $new_test);
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
process_source_tags($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $new_test, $new_test);
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (PHP_SAPI != 'cli') {
|
2020-02-03 20:41:31 +08:00
|
|
|
error('This script is only suited for CLI');
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($argc < 1) {
|
2020-02-03 20:41:31 +08:00
|
|
|
print_help();
|
|
|
|
exit;
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
$options = process_args($argv, $argc);
|
|
|
|
|
|
|
|
if (!$options['dir'] || !is_dir($options['dir'])) {
|
2020-02-03 20:41:31 +08:00
|
|
|
error('The selected output directory does not exist');
|
2017-07-23 16:36:35 +08:00
|
|
|
} else if (is_dir($options['dir'] . $options['ext'])) {
|
2020-02-03 20:41:31 +08:00
|
|
|
error('There is already a folder named "' . $options['ext'] . '" in the output directory');
|
2017-07-23 16:36:35 +08:00
|
|
|
} else if (!mkdir($options['dir'] . $options['ext'])) {
|
2020-02-03 20:41:31 +08:00
|
|
|
error('Unable to create extension directory in the output directory');
|
2017-07-23 16:36:35 +08:00
|
|
|
} else if (!mkdir($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . 'tests')) {
|
2020-02-03 20:41:31 +08:00
|
|
|
error('Unable to create the tests directory');
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($options['experimental']) {
|
2020-02-03 20:41:31 +08:00
|
|
|
print('Creating EXPERIMENTAL... ');
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
if (file_put_contents($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . 'EXPERIMENTAL', '') === false) {
|
|
|
|
error('Unable to create the EXPERIMENTAL file');
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
printf('done%s', PHP_EOL);
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($options['author'])) {
|
2020-02-03 20:41:31 +08:00
|
|
|
print('Creating CREDITS... ');
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
if (!file_put_contents($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . 'CREDITS', $options['ext'] . PHP_EOL . $options['author'])) {
|
|
|
|
error('Unable to create the CREDITS file');
|
|
|
|
}
|
2017-07-23 16:36:35 +08:00
|
|
|
|
2020-02-03 20:41:31 +08:00
|
|
|
printf('done%s', PHP_EOL);
|
2017-07-23 16:36:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
date_default_timezone_set('UTC');
|
|
|
|
|
|
|
|
task('Copying config scripts', 'copy_config_scripts');
|
|
|
|
task('Copying sources', 'copy_sources');
|
|
|
|
task('Copying tests', 'copy_tests');
|
|
|
|
|
|
|
|
print_success();
|