1#!/usr/bin/env php 2<?php 3/* 4 +----------------------------------------------------------------------+ 5 | PHP Version 7 | 6 +----------------------------------------------------------------------+ 7 | Copyright (c) 1997-2018 The PHP Group | 8 +----------------------------------------------------------------------+ 9 | This source file is subject to version 3.01 of the PHP license, | 10 | that is bundled with this package in the file LICENSE, and is | 11 | available through the world-wide-web at the following url: | 12 | http://www.php.net/license/3_01.txt | 13 | If you did not receive a copy of the PHP license and are unable to | 14 | obtain it through the world-wide-web, please send a note to | 15 | license@php.net so we can mail you a copy immediately. | 16 +----------------------------------------------------------------------+ 17 | Authors: Kalle Sommer Nielsen <kalle@php.net> | 18 +----------------------------------------------------------------------+ 19*/ 20 21/* $Id: 0e22659a6a07da486819c4cdc3722be607076bb8 $ */ 22 23/* {{{ error 24 */ 25function error($message) { 26 printf('Error: %s%s', $message, PHP_EOL); 27 exit; 28} 29/* }}} */ 30 31/* {{{ print_help 32 */ 33function print_help() { 34 printf('php ext_skel.php --ext <name> [--experimental] [--author <name>]%s', PHP_EOL); 35 printf(' [--dir <path>] [--std] [--onlyunix]%s', PHP_EOL); 36 printf(' [--onlywindows] [--help]%1$s%1$s', PHP_EOL); 37 printf(' --ext <name> The name of the extension defined as <name>%s', PHP_EOL); 38 printf(' --experimental Passed if this extension is experimental, this creates%s', PHP_EOL); 39 printf(' the EXPERIMENTAL file in the root of the extension%s', PHP_EOL); 40 printf(' --author <name> Your name, this is used if --std is passed and%s', PHP_EOL); 41 printf(' for the CREDITS file%s', PHP_EOL); 42 printf(' --dir <path> Path to the directory for where extension should be%s', PHP_EOL); 43 printf(' created. Defaults to the directory of where this script%s', PHP_EOL); 44 printf(' lives%s', PHP_EOL); 45 printf(' --std If passed, the standard header and vim rules footer used%s', PHP_EOL); 46 printf(' in extensions that is included in the core, will be used%s', PHP_EOL); 47 printf(' --onlyunix Only generate configure scripts for Unix%s', PHP_EOL); 48 printf(' --onlywindows Only generate configure scripts for Windows%s', PHP_EOL); 49 printf(' --help This help%s', PHP_EOL); 50 51 exit; 52} 53/* }}} */ 54 55/* {{{ task 56 */ 57function task($label, $callback) { 58 printf('%s... ', $label); 59 60 $callback(); 61 62 printf('done%s', PHP_EOL); 63} 64/* }}} */ 65 66/* {{{ print_success 67 */ 68function print_success() { 69 global $options; 70 71 if (PHP_OS_FAMILY != 'Windows') { 72 $file_prefix = './'; 73 $make_prefix = ''; 74 } else { 75 $file_prefix = ''; 76 $make_prefix = 'n'; 77 } 78 79 printf('%1$sSuccess. The extension is now ready to be compiled into PHP. To do so, use the%s', PHP_EOL); 80 printf('following steps:%1$s%1$s', PHP_EOL); 81 printf('cd /path/to/php-src%s', PHP_EOL); 82 printf('%sbuildconf%s', $file_prefix, PHP_EOL); 83 printf('%sconfigure --enable-%s%s', $file_prefix, $options['ext'], PHP_EOL); 84 printf('%smake%2$s%2$s', $make_prefix, PHP_EOL); 85 printf('Don\'t forget to run tests once the compilation is done:%s', PHP_EOL); 86 printf('%smake test TESTS=ext/%s/tests%3$s%3$s', $make_prefix, $options['ext'], PHP_EOL); 87 printf('Thank you for using PHP!%s', PHP_EOL); 88} 89/* }}} */ 90 91/* {{{ process_args 92 */ 93function process_args($argv, $argc) { 94 $options = [ 95 'unix' => true, 96 'windows' => true, 97 'ext' => '', 98 'dir' => __DIR__ . DIRECTORY_SEPARATOR, 99 'skel' => __DIR__ . DIRECTORY_SEPARATOR . 'skeleton' . DIRECTORY_SEPARATOR, 100 'author' => false, 101 'experimental' => false, 102 'std' => false 103 ]; 104 105 for($i = 1; $i < $argc; ++$i) 106 { 107 $val = $argv[$i]; 108 109 if($val{0} != '-' || $val{1} != '-') 110 { 111 continue; 112 } 113 114 switch($opt = strtolower(substr($val, 2))) 115 { 116 case 'help': { 117 print_help(); 118 } 119 case 'onlyunix': { 120 $options['windows'] = false; 121 } 122 break; 123 case 'onlywindows': { 124 $options['unix'] = false; 125 } 126 break; 127 case 'experimental': { 128 $options['experimental'] = true; 129 } 130 break; 131 case 'std': { 132 $options['std'] = true; 133 } 134 break; 135 case 'ext': 136 case 'dir': 137 case 'author': { 138 if (!isset($argv[$i + 1]) || ($argv[$i + 1]{0} == '-' && $argv[$i + 1]{1} == '-')) { 139 error('Argument "' . $val . '" expects a value, none passed'); 140 } else if ($opt == 'dir' && empty($argv[$i + 1])) { 141 continue 2; 142 } 143 144 $options[$opt] = ($opt == 'dir' ? realpath($argv[$i + 1]) . DIRECTORY_SEPARATOR : $argv[$i + 1]); 145 } 146 break; 147 default: { 148 error('Unsupported argument "' . $val . '" passed'); 149 } 150 } 151 } 152 153 if (empty($options['ext'])) { 154 error('No extension name passed, use "--ext <name>"'); 155 } else if (!$options['unix'] && !$options['windows']) { 156 error('Cannot pass both --onlyunix and --onlywindows'); 157 } else if (!is_dir($options['skel'])) { 158 error('The skeleton directory was not found'); 159 } 160 161 $options['ext'] = str_replace(['\\', '/'], '', strtolower($options['ext'])); 162 163 return $options; 164} 165/* }}} */ 166 167/* {{{ process_source_tags 168 */ 169function process_source_tags($file, $short_name) { 170 global $options; 171 172 $source = file_get_contents($file); 173 174 if ($source === false) { 175 error('Unable to open file for reading: ' . $short_name); 176 } 177 178 $source = str_replace('%EXTNAME%', $options['ext'], $source); 179 $source = str_replace('%EXTNAMECAPS%', strtoupper($options['ext']), $source); 180 181 if (strpos($short_name, '.c') !== false || strpos($short_name, '.h') !== false) { 182 static $header, $footer; 183 184 if (!$header) { 185 if ($options['std']) { 186 $year = date('Y'); 187 $author_len = strlen($options['author']); 188 $credits = $options['author'] . ($author_len && $author_len <= 60 ? str_repeat(' ', 60 - $author_len) : ''); 189 190 $header = <<<"HEADER" 191/* 192 +----------------------------------------------------------------------+ 193 | PHP Version 7 | 194 +----------------------------------------------------------------------+ 195 | Copyright (c) 1997-$year The PHP Group | 196 +----------------------------------------------------------------------+ 197 | This source file is subject to version 3.01 of the PHP license, | 198 | that is bundled with this package in the file LICENSE, and is | 199 | available through the world-wide-web at the following url: | 200 | http://www.php.net/license/3_01.txt | 201 | If you did not receive a copy of the PHP license and are unable to | 202 | obtain it through the world-wide-web, please send a note to | 203 | license@php.net so we can mail you a copy immediately. | 204 +----------------------------------------------------------------------+ 205 | Author: $credits | 206 +----------------------------------------------------------------------+ 207*/ 208HEADER; 209 $footer = <<<'FOOTER' 210 211/* 212 * Local variables: 213 * tab-width: 4 214 * c-basic-offset: 4 215 * End: 216 */ 217FOOTER; 218 } else { 219 if ($options['author']) { 220 $header = sprintf('/* %s extension for PHP (c) %d %s */', $options['ext'], date('Y'), $options['author']); 221 } else { 222 $header = sprintf('/* %s extension for PHP */', $options['ext']); 223 } 224 225 $footer = ''; 226 } 227 } 228 229 $source = str_replace(['%HEADER%', '%FOOTER%'], [$header, $footer], $source); 230 } 231 232 if (!file_put_contents($file, $source)) { 233 error('Unable to save contents to file: ' . $short_name); 234 } 235} 236/* }}} */ 237 238/* {{{ copy_config_scripts 239 */ 240function copy_config_scripts() { 241 global $options; 242 243 $files = []; 244 245 if ($options['unix']) { 246 $files[] = 'config.m4'; 247 } 248 249 if ($options['windows']) { 250 $files[] = 'config.w32'; 251 } 252 253 $files[] = '.gitignore'; 254 255 foreach($files as $config_script) { 256 $new_config_script = $options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $config_script; 257 258 if (!copy($options['skel'] . $config_script . '.in', $new_config_script)) { 259 error('Unable to copy config script: ' . $config_script); 260 } 261 262 process_source_tags($new_config_script, $config_script); 263 } 264} 265/* }}} */ 266 267/* {{{ copy_sources 268 */ 269function copy_sources() { 270 global $options; 271 272 $files = [ 273 'skeleton.c' => $options['ext'] . '.c', 274 'php_skeleton.h' => 'php_' . $options['ext'] . '.h' 275 ]; 276 277 foreach ($files as $src_file => $dst_file) { 278 if (!copy($options['skel'] . $src_file, $options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $dst_file)) { 279 error('Unable to copy source file: ' . $src_file); 280 } 281 282 process_source_tags($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $dst_file, $dst_file); 283 } 284} 285/* }}} */ 286 287/* {{{ copy_tests 288 */ 289function copy_tests() { 290 global $options; 291 292 $test_files = glob($options['skel'] . 'tests/*', GLOB_MARK); 293 294 if (!$test_files) { 295 return; 296 } 297 298 foreach ($test_files as $test) { 299 if (is_dir($test)) { 300 continue; 301 } 302 303 $new_test = str_replace([$options['skel'], '/'], ['', DIRECTORY_SEPARATOR], $test); 304 305 if (!copy($test, $options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $new_test)) { 306 error('Unable to copy file: ' . $new_test); 307 } 308 309 process_source_tags($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $new_test, $new_test); 310 } 311} 312/* }}} */ 313 314 315if (PHP_SAPI != 'cli') { 316 error('This script is only suited for CLI'); 317} 318 319if ($argc < 1) { 320 print_help(); 321 exit; 322} 323 324$options = process_args($argv, $argc); 325 326if (!$options['dir'] || !is_dir($options['dir'])) { 327 error('The selected output directory does not exist'); 328} else if (is_dir($options['dir'] . $options['ext'])) { 329 error('There is already a folder named "' . $options['ext'] . '" in the output directory'); 330} else if (!mkdir($options['dir'] . $options['ext'])) { 331 error('Unable to create extension directory in the output directory'); 332} else if (!mkdir($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . 'tests')) { 333 error('Unable to create the tests directory'); 334} 335 336if ($options['experimental']) { 337 print('Creating EXPERIMENTAL... '); 338 339 if (file_put_contents($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . 'EXPERIMENTAL', '') === false) { 340 error('Unable to create the EXPERIMENTAL file'); 341 } 342 343 printf('done%s', PHP_EOL); 344} 345 346if (!empty($options['author'])) { 347 print('Creating CREDITS... '); 348 349 if (!file_put_contents($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . 'CREDITS', $options['ext'] . PHP_EOL . $options['author'])) { 350 error('Unable to create the CREDITS file'); 351 } 352 353 printf('done%s', PHP_EOL); 354} 355 356date_default_timezone_set('UTC'); 357 358task('Copying config scripts', 'copy_config_scripts'); 359task('Copying sources', 'copy_sources'); 360task('Copying tests', 'copy_tests'); 361 362print_success(); 363