php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
ext_skel.php
Go to the documentation of this file.
1#!/usr/bin/env php
2<?php
3/*
4 +----------------------------------------------------------------------+
5 | Copyright (c) The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | https://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Kalle Sommer Nielsen <kalle@php.net> |
16 +----------------------------------------------------------------------+
17*/
18
19/* $Id: ae7a3a987c0a9a6a0d4d07cc79eed0afa15e79dc $ */
20
21/* {{{ error */
22function error($message) {
23 printf('Error: %s%s', $message, PHP_EOL);
24 exit;
25}
26/* }}} */
27
28/* {{{ print_help */
29function print_help() {
30 if (PHP_OS_FAMILY != 'Windows') {
31 $file_prefix = './';
32 $make_prefix = '';
33 } else {
34 $file_prefix = '';
35 $make_prefix = 'n';
36 }
37
38 echo <<<HELP
39WHAT IT IS
40
41 It's a tool for automatically creating the basic framework for a PHP extension.
42
43HOW TO USE IT
44
45 Very simple. First, change to the ext/ directory of the PHP sources. Then run
46 the following
47
48 php ext_skel.php --ext extension_name
49
50 and everything you need will be placed in directory ext/extension_name.
51
52 If you don't need to test the existence of any external header files,
53 libraries or functions in them, the extension is ready to be compiled in PHP.
54 To compile the extension run the following:
55
56 cd extension_name
57 phpize
58 {$file_prefix}configure
59 {$make_prefix}make
60
61 Don't forget to run tests once the compilation is done:
62
63 {$make_prefix}make test
64
65 Alternatively, to compile extension in the PHP:
66
67 cd /path/to/php-src
68 {$file_prefix}buildconf
69 {$file_prefix}configure --enable-extension_name
70 {$make_prefix}make
71 {$make_prefix}make test TESTS=ext/extension_name/tests
72
73 The definition of PHP_extension_NAME_VERSION will be present in the
74 php_extension_name.h and injected into the zend_extension_entry definition.
75 This is required by the PECL website for the version string conformity checks
76 against package.xml
77
78SOURCE AND HEADER FILE NAME
79
80 The ext_skel.php script generates 'extension_name.c' and 'php_extension_name.h'
81 as the main source and header files. Keep these names.
82
83 extension functions (User functions) must be named
84
85 extension_name_function()
86
87 When you need to expose extension functions to other extensions, expose
88 functions strictly needed by others. Exposed internal function must be named
89
90 php_extension_name_function()
91
92 See also CODING_STANDARDS.md.
93
94OPTIONS
95
96 php ext_skel.php --ext <name> [--experimental] [--author <name>]
97 [--dir <path>] [--std] [--onlyunix]
98 [--onlywindows] [--help]
99
100 --ext <name> The name of the extension defined as <name>
101 --experimental Passed if this extension is experimental, this creates
102 the EXPERIMENTAL file in the root of the extension
103 --author <name> Your name, this is used if --std is passed and for the
104 CREDITS file
105 --dir <path> Path to the directory for where extension should be
106 created. Defaults to the directory of where this script
107 lives
108 --std If passed, the standard header used in extensions that
109 is included in the core, will be used
110 --onlyunix Only generate configure scripts for Unix
111 --onlywindows Only generate configure scripts for Windows
112 --help This help
113
114HELP;
115 exit;
116}
117/* }}} */
118
119/* {{{ task */
120function task($label, $callback) {
121 printf('%s... ', $label);
122
123 $callback();
124
125 printf('done%s', PHP_EOL);
126}
127/* }}} */
128
129/* {{{ print_success */
130function print_success() {
131 global $options;
132
133 if (PHP_OS_FAMILY != 'Windows') {
134 $file_prefix = './';
135 $make_prefix = '';
136 } else {
137 $file_prefix = '';
138 $make_prefix = 'n';
139 }
140
141 printf('%1$sSuccess. The extension is now ready to be compiled. To do so, use the%s', PHP_EOL);
142 printf('following steps:%1$s%1$s', PHP_EOL);
143 printf('cd %s%s%s', $options['dir'], $options['ext'], PHP_EOL);
144 printf('phpize%s', PHP_EOL);
145 printf('%sconfigure%s', $file_prefix, PHP_EOL);
146 printf('%smake%2$s%2$s', $make_prefix, PHP_EOL);
147 printf('Don\'t forget to run tests once the compilation is done:%s', PHP_EOL);
148 printf('%smake test%2$s%2$s', $make_prefix, PHP_EOL);
149 printf('Thank you for using PHP!%s', PHP_EOL);
150}
151/* }}} */
152
153/* {{{ process_args */
154function process_args($argv, $argc) {
155 $options = [
156 'unix' => true,
157 'windows' => true,
158 'ext' => '',
159 'dir' => __DIR__ . DIRECTORY_SEPARATOR,
160 'skel' => __DIR__ . DIRECTORY_SEPARATOR . 'skeleton' . DIRECTORY_SEPARATOR,
161 'author' => false,
162 'experimental' => false,
163 'std' => false
164 ];
165
166 for($i = 1; $i < $argc; ++$i)
167 {
168 $val = $argv[$i];
169
170 if($val[0] != '-' || $val[1] != '-')
171 {
172 continue;
173 }
174
175 switch($opt = strtolower(substr($val, 2)))
176 {
177 case 'help': {
178 print_help();
179 }
180 case 'onlyunix': {
181 $options['windows'] = false;
182 }
183 break;
184 case 'onlywindows': {
185 $options['unix'] = false;
186 }
187 break;
188 case 'experimental': {
189 $options['experimental'] = true;
190 }
191 break;
192 case 'std': {
193 $options['std'] = true;
194 }
195 break;
196 case 'ext':
197 case 'dir':
198 case 'author': {
199 if (!isset($argv[$i + 1]) || ($argv[$i + 1][0] == '-' && $argv[$i + 1][1] == '-')) {
200 error('Argument "' . $val . '" expects a value, none passed');
201 } else if ($opt == 'dir' && empty($argv[$i + 1])) {
202 continue 2;
203 }
204
205 $options[$opt] = ($opt == 'dir' ? realpath($argv[$i + 1]) . DIRECTORY_SEPARATOR : $argv[$i + 1]);
206 }
207 break;
208 default: {
209 error('Unsupported argument "' . $val . '" passed');
210 }
211 }
212 }
213
214 if (empty($options['ext'])) {
215 error('No extension name passed, use "--ext <name>"');
216 } else if (!$options['unix'] && !$options['windows']) {
217 error('Cannot pass both --onlyunix and --onlywindows');
218 } else if (!is_dir($options['skel'])) {
219 error('The skeleton directory was not found');
220 }
221
222 // Validate extension name
223 if (!preg_match('/^[a-z][a-z0-9_]+$/i', $options['ext'])) {
224 error('Invalid extension name. Valid names start with a letter,'
225 .' followed by any number of letters, numbers, or underscores.'
226 .' Using only lower case letters is preferred.');
227 }
228
229 $options['ext'] = str_replace(['\\', '/'], '', strtolower($options['ext']));
230
231 return $options;
232}
233/* }}} */
234
235/* {{{ process_source_tags */
236function process_source_tags($file, $short_name) {
237 global $options;
238
240
241 if ($source === false) {
242 error('Unable to open file for reading: ' . $short_name);
243 }
244
245 $source = str_replace('%EXTNAME%', $options['ext'], $source);
246 $source = str_replace('%EXTNAMECAPS%', strtoupper($options['ext']), $source);
247
248 if (strpos($short_name, '.c') !== false || strpos($short_name, '.h') !== false) {
249 static $header;
250
251 if (!$header) {
252 if ($options['std']) {
253 $author_len = strlen($options['author']);
254 $credits = $options['author'] . ($author_len && $author_len <= 60 ? str_repeat(' ', 60 - $author_len) : '');
255
256 $header = <<<"HEADER"
257/*
258 +----------------------------------------------------------------------+
259 | Copyright (c) The PHP Group |
260 +----------------------------------------------------------------------+
261 | This source file is subject to version 3.01 of the PHP license, |
262 | that is bundled with this package in the file LICENSE, and is |
263 | available through the world-wide-web at the following url: |
264 | https://www.php.net/license/3_01.txt |
265 | If you did not receive a copy of the PHP license and are unable to |
266 | obtain it through the world-wide-web, please send a note to |
267 | license@php.net so we can mail you a copy immediately. |
268 +----------------------------------------------------------------------+
269 | Author: $credits |
270 +----------------------------------------------------------------------+
271*/
272HEADER;
273 } else {
274 if ($options['author']) {
275 $header = sprintf('/* %s extension for PHP (c) %d %s */', $options['ext'], date('Y'), $options['author']);
276 } else {
277 $header = sprintf('/* %s extension for PHP */', $options['ext']);
278 }
279 }
280 }
281
282 $source = str_replace('%HEADER%', $header, $source);
283 }
284
286 error('Unable to save contents to file: ' . $short_name);
287 }
288}
289/* }}} */
290
291/* {{{ copy_config_scripts */
293 global $options;
294
295 $files = [];
296
297 if ($options['unix']) {
298 $files[] = 'config.m4';
299 }
300
301 if ($options['windows']) {
302 $files[] = 'config.w32';
303 }
304
305 $files[] = '.gitignore';
306
307 foreach($files as $config_script) {
308 $new_config_script = $options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $config_script;
309
310 if (!copy($options['skel'] . $config_script . '.in', $new_config_script)) {
311 error('Unable to copy config script: ' . $config_script);
312 }
313
314 process_source_tags($new_config_script, $config_script);
315 }
316}
317/* }}} */
318
319/* {{{ copy_sources */
320function copy_sources() {
321 global $options;
322
323 $files = [
324 'skeleton.c' => $options['ext'] . '.c',
325 'skeleton.stub.php' => $options['ext'] . '.stub.php',
326 'php_skeleton.h' => 'php_' . $options['ext'] . '.h',
327 'skeleton_arginfo.h' => $options['ext'] . '_arginfo.h'
328 ];
329
330 foreach ($files as $src_file => $dst_file) {
331 if (!copy($options['skel'] . $src_file, $options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $dst_file)) {
332 error('Unable to copy source file: ' . $src_file);
333 }
334
335 process_source_tags($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $dst_file, $dst_file);
336 }
337}
338/* }}} */
339
340/* {{{ copy_tests */
341function copy_tests() {
342 global $options;
343
344 $test_files = glob($options['skel'] . 'tests/*', GLOB_MARK);
345
346 if (!$test_files) {
347 return;
348 }
349
350 foreach ($test_files as $test) {
351 if (is_dir($test)) {
352 continue;
353 }
354
355 $new_test = str_replace([$options['skel'], '/'], ['', DIRECTORY_SEPARATOR], $test);
356
357 if (!copy($test, $options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $new_test)) {
358 error('Unable to copy file: ' . $new_test);
359 }
360
361 process_source_tags($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $new_test, $new_test);
362 }
363}
364/* }}} */
365
366
367if (PHP_SAPI != 'cli') {
368 error('This script is only suited for CLI');
369}
370
371if ($argc < 1) {
372 print_help();
373 exit;
374}
375
376$options = process_args($argv, $argc);
377
378if (!$options['dir'] || !is_dir($options['dir'])) {
379 error('The selected output directory does not exist');
380} else if (is_dir($options['dir'] . $options['ext'])) {
381 error('There is already a folder named "' . $options['ext'] . '" in the output directory');
382} else if (!mkdir($options['dir'] . $options['ext'])) {
383 error('Unable to create extension directory in the output directory');
384} else if (!mkdir($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . 'tests')) {
385 error('Unable to create the tests directory');
386}
387
388if ($options['experimental']) {
389 print('Creating EXPERIMENTAL... ');
390
391 if (file_put_contents($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . 'EXPERIMENTAL', '') === false) {
392 error('Unable to create the EXPERIMENTAL file');
393 }
394
395 printf('done%s', PHP_EOL);
396}
397
398if (!empty($options['author'])) {
399 print('Creating CREDITS... ');
400
401 if (!file_put_contents($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . 'CREDITS', $options['ext'] . PHP_EOL . $options['author'])) {
402 error('Unable to create the CREDITS file');
403 }
404
405 printf('done%s', PHP_EOL);
406}
407
409
410task('Copying config scripts', 'copy_config_scripts');
411task('Copying sources', 'copy_sources');
412task('Copying tests', 'copy_tests');
413
file_private const char ext[]
str_repeat(string $string, int $times)
is_dir(string $filename)
file_get_contents(string $filename, bool $use_include_path=false, $context=null, int $offset=0, ?int $length=null)
strtoupper(string $string)
printf(string $format, mixed ... $values)
strpos(string $haystack, string $needle, int $offset=0)
str_replace(array|string $search, array|string $replace, string|array $subject, &$count=null)
copy(string $from, string $to, $context=null)
file_put_contents(string $filename, mixed $data, int $flags=0, $context=null)
dir(string $directory, $context=null)
strtolower(string $string)
header(string $header, bool $replace=true, int $response_code=0)
glob(string $pattern, int $flags=0)
mkdir(string $directory, int $permissions=0777, bool $recursive=false, $context=null)
substr(string $string, int $offset, ?int $length=null)
realpath(string $path)
char s[4]
Definition cdf.c:77
print
const DIRECTORY_SEPARATOR
Definition dir.stub.php:9
$file
Definition encryption.php:9
print_help()
Definition ext_skel.php:29
print_success()
Definition ext_skel.php:130
if(PHP_SAPI !='cli') if($argc< 1) $options
Definition ext_skel.php:376
error($message)
Definition ext_skel.php:22
copy_config_scripts()
Definition ext_skel.php:292
copy_sources()
Definition ext_skel.php:320
task($label, $callback)
Definition ext_skel.php:120
process_args($argv, $argc)
Definition ext_skel.php:154
process_source_tags($file, $short_name)
Definition ext_skel.php:236
copy_tests()
Definition ext_skel.php:341
zend_long n
Definition ffi.c:4979
#define GLOB_MARK
Definition glob.h:76
sprintf("0x%X", $numelems)
const PHP_SAPI
$s
Definition makestub.php:2
#define PHP_OS_FAMILY
Definition php.h:51
#define PHP_EOL
Definition php.h:74
date_default_timezone_set(string $timezoneId)
date(string $format, ?int $timestamp=null)
preg_match(string $pattern, string $subject, &$matches=null, int $flags=0, int $offset=0)
HashTable functions
time_t now
Definition session.c:1281
#define IT
$test
Definition test.php:82
test($x, $y=0)
Definition test.php:21
$files
Definition ucgendat.php:59
strlen(string $string)
exit(string|int $status=0)