Checks if the content of the file on the disk is the same as the content.
public static checkAndWriteIfNeeded ( $content, $path ) : boolean | ||
$content | ||
$path | ||
return | boolean |
/** * Create project.c and project.h according to the current extension * * @param string $project * * @throws Exception * @return boolean * TODO: Move the part of the logic which depends on templates (backend-specific) to backend? */ public function createProjectFiles($project) { $needConfigure = $this->checkKernelFiles(); $templatePath = $this->backend->getInternalTemplatePath(); /** * project.c */ $content = file_get_contents($templatePath . '/project.c'); if (empty($content)) { throw new Exception("Template project.c doesn't exist"); } $includes = ''; $destructors = ''; $files = array_merge($this->files, $this->anonymousFiles); /** * Round 1. Calculate the dependency rank */ $this->calculateDependencies($files); $classEntries = array(); $classInits = array(); $interfaceEntries = array(); $interfaceInits = array(); /** * Round 2. Generate the ZEPHIR_INIT calls according to the dependency rank */ foreach ($files as $file) { if (!$file->isExternal()) { $classDefinition = $file->getClassDefinition(); if ($classDefinition) { $dependencyRank = $classDefinition->getDependencyRank(); if ($classDefinition->getType() == 'class') { if (!isset($classInits[$dependencyRank])) { $classEntries[$dependencyRank] = array(); $classInits[$dependencyRank] = array(); } $classEntries[$dependencyRank][] = 'zend_class_entry *' . $classDefinition->getClassEntry() . ';'; $classInits[$dependencyRank][] = 'ZEPHIR_INIT(' . $classDefinition->getCNamespace() . '_' . $classDefinition->getName() . ');'; } else { if (!isset($interfaceInits[$dependencyRank])) { $interfaceEntries[$dependencyRank] = array(); $interfaceInits[$dependencyRank] = array(); } $interfaceEntries[$dependencyRank][] = 'zend_class_entry *' . $classDefinition->getClassEntry() . ';'; $interfaceInits[$dependencyRank][] = 'ZEPHIR_INIT(' . $classDefinition->getCNamespace() . '_' . $classDefinition->getName() . ');'; } } } } krsort($classInits); krsort($classEntries); krsort($interfaceInits); krsort($interfaceEntries); $completeInterfaceInits = array(); foreach ($interfaceInits as $rankInterfaceInits) { asort($rankInterfaceInits, SORT_STRING); $completeInterfaceInits = array_merge($completeInterfaceInits, $rankInterfaceInits); } $completeInterfaceEntries = array(); foreach ($interfaceEntries as $rankInterfaceEntries) { asort($rankInterfaceEntries, SORT_STRING); $completeInterfaceEntries = array_merge($completeInterfaceEntries, $rankInterfaceEntries); } $completeClassInits = array(); foreach ($classInits as $rankClassInits) { asort($rankClassInits, SORT_STRING); $completeClassInits = array_merge($completeClassInits, $rankClassInits); } $completeClassEntries = array(); foreach ($classEntries as $rankClassEntries) { asort($rankClassEntries, SORT_STRING); $completeClassEntries = array_merge($completeClassEntries, $rankClassEntries); } /** * Round 3. Process extension globals */ list($globalCode, $globalStruct, $globalsDefault, $initEntries) = $this->processExtensionGlobals($project); if ($project == 'zend') { $safeProject = 'zend_'; } else { $safeProject = $project; } /** * Round 4. Process extension info */ $phpInfo = $this->processExtensionInfo(); /** * Round 5. Generate Function entries (FE) */ list($feHeader, $feEntries) = $this->generateFunctionInformation(); /** * Check if there are module/request/global destructors */ $destructors = $this->config->get('destructors'); if (is_array($destructors)) { $invokeDestructors = $this->processCodeInjection($destructors); $includes = $invokeDestructors[0]; $destructors = $invokeDestructors[1]; } /** * Check if there are module/request/global initializers */ $initializers = $this->config->get('initializers'); if (is_array($initializers)) { $invokeInitializers = $this->processCodeInjection($initializers); $includes = $invokeInitializers[0]; $initializers = $invokeInitializers[1]; } /** * Append extra details */ $extraClasses = $this->config->get('extra-classes'); if (is_array($extraClasses)) { foreach ($extraClasses as $value) { if (isset($value['init'])) { $completeClassInits[] = 'ZEPHIR_INIT(' . $value['init'] . ')'; } if (isset($value['entry'])) { $completeClassEntries[] = 'zend_class_entry *' . $value['entry'] . ';'; } } } $toReplace = array('%PROJECT_LOWER_SAFE%' => strtolower($safeProject), '%PROJECT_LOWER%' => strtolower($project), '%PROJECT_UPPER%' => strtoupper($project), '%PROJECT_CAMELIZE%' => ucfirst($project), '%CLASS_ENTRIES%' => implode(PHP_EOL, array_merge($completeInterfaceEntries, $completeClassEntries)), '%CLASS_INITS%' => implode(PHP_EOL . "\t", array_merge($completeInterfaceInits, $completeClassInits)), '%INIT_GLOBALS%' => $globalsDefault, '%EXTENSION_INFO%' => $phpInfo, '%EXTRA_INCLUDES%' => $includes, '%DESTRUCTORS%' => $destructors, '%INITIALIZERS%' => implode(PHP_EOL, array_merge($this->internalInitializers, array($initializers))), '%FE_HEADER%' => $feHeader, '%FE_ENTRIES%' => $feEntries, '%PROJECT_INI_ENTRIES%' => implode(PHP_EOL . "\t", $initEntries)); foreach ($toReplace as $mark => $replace) { $content = str_replace($mark, $replace, $content); } /** * Round 5. Generate and place the entry point of the project */ Utils::checkAndWriteIfNeeded($content, 'ext/' . $safeProject . '.c'); unset($content); /** * Round 6. Generate the project main header */ $content = file_get_contents($templatePath . '/project.h'); if (empty($content)) { throw new Exception("Template project.h doesn't exists"); } $includeHeaders = array(); foreach ($this->compiledFiles as $file) { if ($file) { $fileH = str_replace(".c", ".zep.h", $file); $include = '#include "' . $fileH . '"'; $includeHeaders[] = $include; } } /** * Append extra headers */ $extraClasses = $this->config->get('extra-classes'); if (is_array($extraClasses)) { foreach ($extraClasses as $value) { if (isset($value['header'])) { $include = '#include "' . $value['header'] . '"'; $includeHeaders[] = $include; } } } $toReplace = array('%INCLUDE_HEADERS%' => implode(PHP_EOL, $includeHeaders)); foreach ($toReplace as $mark => $replace) { $content = str_replace($mark, $replace, $content); } Utils::checkAndWriteIfNeeded($content, 'ext/' . $safeProject . '.h'); unset($content); /** * Round 7. Create php_project.h */ $content = file_get_contents($templatePath . '/php_project.h'); if (empty($content)) { throw new Exception('Template php_project.h doesn`t exist'); } $toReplace = array('%PROJECT_LOWER_SAFE%' => strtolower($safeProject), '%PROJECT_LOWER%' => strtolower($project), '%PROJECT_UPPER%' => strtoupper($project), '%PROJECT_EXTNAME%' => strtolower($project), '%PROJECT_NAME%' => utf8_decode($this->config->get('name')), '%PROJECT_AUTHOR%' => utf8_decode($this->config->get('author')), '%PROJECT_VERSION%' => utf8_decode($this->config->get('version')), '%PROJECT_DESCRIPTION%' => utf8_decode($this->config->get('description')), '%PROJECT_ZEPVERSION%' => self::VERSION, '%EXTENSION_GLOBALS%' => $globalCode, '%EXTENSION_STRUCT_GLOBALS%' => $globalStruct); foreach ($toReplace as $mark => $replace) { $content = str_replace($mark, $replace, $content); } Utils::checkAndWriteIfNeeded($content, 'ext/php_' . $safeProject . '.h'); unset($content); return $needConfigure; }
public function genFcallCode() { $codePrinter = new CodePrinter(); $codePrinter->output('#ifndef ZEPHIR_KERNEL_FCALL_INTERNAL_H'); $codePrinter->output('#define ZEPHIR_KERNEL_FCALL_INTERNAL_H'); $codePrinter->increaseLevel(); ksort($this->requiredMacros); foreach ($this->requiredMacros as $name => $info) { list($scope, $mode, $paramCount) = $info; $paramsStr = ''; $retParam = ''; $retValueUsed = '0'; $params = array(); $zvals = array(); $initStatements = array(); $postStatements = array(); for ($i = 0; $i < $paramCount; ++$i) { $params[] = 'p' . $i; } if ($paramCount) { $paramsStr = ', ' . implode(', ', $params); } if ($mode == 'CALL_INTERNAL_METHOD_P') { $retValueUsed = '1'; $retParam = 'return_value_ptr'; $initStatements[] = 'ZEPHIR_INIT_NVAR(*(return_value_ptr)); \\'; } $objParam = $scope ? 'scope_ce, ' : 'object, '; $macroName = $name . '(' . ($retParam ? $retParam . ', ' : '') . $objParam . 'method' . $paramsStr . ')'; $codePrinter->output('#define ' . $macroName . ' \\'); if (!$retParam) { $retParam = 'return_value'; } $codePrinter->increaseLevel(); $codePrinter->output('do { \\'); $codePrinter->increaseLevel(); if ($mode == 'CALL_INTERNAL_METHOD_NORETURN_P') { $codePrinter->output('zval *rv = NULL; \\'); $codePrinter->output('zval **rvp = &rv; \\'); $codePrinter->output('ALLOC_INIT_ZVAL(rv); \\'); $retParam = 'rvp'; } $codePrinter->output('ZEPHIR_BACKUP_SCOPE() \\'); $codePrinter->output('ZEPHIR_BACKUP_THIS_PTR() \\'); if (!$scope) { $codePrinter->output('ZEPHIR_SET_THIS(object); \\'); $codePrinter->output('ZEPHIR_SET_SCOPE((Z_TYPE_P(object) == IS_OBJECT ? Z_OBJCE_P(object) : NULL), (Z_TYPE_P(object) == IS_OBJECT ? Z_OBJCE_P(object) : NULL)); \\'); } else { $codePrinter->output('ZEPHIR_SET_THIS(NULL); \\'); $codePrinter->output('ZEPHIR_SET_SCOPE(scope_ce, scope_ce); \\'); } /* Create new zval's for parameters */ for ($i = 0; $i < $paramCount; ++$i) { //$zv = '_' . $params[$i]; //$zvals[] = $zv; //$initStatements[] = 'ALLOC_ZVAL(' . $zv . '); \\'; //$initStatements[] = 'INIT_PZVAL_COPY(' . $zv . ', ' . $params[$i] . '); \\'; //$postStatements[] = 'zval_ptr_dtor(&' . $zv . '); \\'; $zv = $params[$i]; $zvals[] = $zv; $initStatements[] = 'Z_ADDREF_P(' . $zv . '); \\'; $postStatements[] = 'Z_DELREF_P(' . $zv . '); \\'; } if ($i) { //$codePrinter->output('zval *' . implode(', *', $zvals) . '; \\'); } foreach ($initStatements as $statement) { $codePrinter->output($statement); } $zvalStr = $i ? ', ' . implode(', ', $zvals) : ''; $retExpr = ''; if ($retParam) { if ($retParam == 'return_value') { $retExpr = ', return_value, return_value_ptr'; } else { $retExpr = ', *' . $retParam . ', ' . $retParam; } } $codePrinter->output('method(0' . $retExpr . ', ' . ($scope ? 'NULL, ' : $objParam) . $retValueUsed . $zvalStr . ' TSRMLS_CC); \\'); if ($mode == 'CALL_INTERNAL_METHOD_NORETURN_P') { $postStatements[] = 'zval_ptr_dtor(rvp); \\'; } foreach ($postStatements as $statement) { $codePrinter->output($statement); } $codePrinter->output('ZEPHIR_LAST_CALL_STATUS = EG(exception) ? FAILURE : SUCCESS; \\'); $codePrinter->output('ZEPHIR_RESTORE_THIS_PTR(); \\'); $codePrinter->output('ZEPHIR_RESTORE_SCOPE(); \\'); $codePrinter->decreaseLevel(); $codePrinter->output('} while (0)'); $codePrinter->decreaseLevel(); $codePrinter->output(''); } $codePrinter->decreaseLevel(); $codePrinter->output("#endif"); Utils::checkAndWriteIfNeeded($codePrinter->getOutput(), 'ext/kernel/fcall_internal.h'); }
/** * Create project.c and project.h by compiled files to test extension * * @param string $project * * @throws Exception * @return boolean */ public function createProjectFiles($project) { $needConfigure = $this->checkKernelFiles(); /** * project.c */ $content = file_get_contents(__DIR__ . '/../templates/project.c'); if (empty($content)) { throw new Exception("Template project.c doesn't exist"); } $files = $this->files; /** * Round 1. Calculate the dependency rank * Classes are ordered according to a dependency ranking * Classes that are dependencies of classes that are dependency of other classes * have more weight */ foreach ($files as $file) { $classDefinition = $file->getClassDefinition(); if ($classDefinition) { $classDefinition->calculateDependencyRank(); } } /** * Round 1.5 Make a second pass to ensure classes will have the correct weight */ foreach ($files as $file) { $classDefinition = $file->getClassDefinition(); if ($classDefinition) { $classDefinition->calculateDependencyRank(); } } $classEntries = array(); $classInits = array(); $interfaceEntries = array(); $interfaceInits = array(); /** * Round 2. Generate the ZEPHIR_INIT calls according to the dependency rank */ foreach ($files as $file) { $classDefinition = $file->getClassDefinition(); if ($classDefinition) { $dependencyRank = $classDefinition->getDependencyRank(); if ($classDefinition->getType() == 'class') { if (!isset($classInits[$dependencyRank])) { $classEntries[$dependencyRank] = array(); $classInits[$dependencyRank] = array(); } $classEntries[$dependencyRank][] = 'zend_class_entry *' . $classDefinition->getClassEntry() . ';'; $classInits[$dependencyRank][] = 'ZEPHIR_INIT(' . $classDefinition->getCNamespace() . '_' . $classDefinition->getName() . ');'; } else { if (!isset($interfaceInits[$dependencyRank])) { $interfaceEntries[$dependencyRank] = array(); $interfaceInits[$dependencyRank] = array(); } $interfaceEntries[$dependencyRank][] = 'zend_class_entry *' . $classDefinition->getClassEntry() . ';'; $interfaceInits[$dependencyRank][] = 'ZEPHIR_INIT(' . $classDefinition->getCNamespace() . '_' . $classDefinition->getName() . ');'; } } } krsort($classInits); krsort($classEntries); krsort($interfaceInits); krsort($interfaceEntries); $completeInterfaceInits = array(); foreach ($interfaceInits as $rankInterfaceInits) { asort($rankInterfaceInits, SORT_STRING); $completeInterfaceInits = array_merge($completeInterfaceInits, $rankInterfaceInits); } $completeInterfaceEntries = array(); foreach ($interfaceEntries as $rankInterfaceEntries) { asort($rankInterfaceEntries, SORT_STRING); $completeInterfaceEntries = array_merge($completeInterfaceEntries, $rankInterfaceEntries); } $completeClassInits = array(); foreach ($classInits as $rankClassInits) { asort($rankClassInits, SORT_STRING); $completeClassInits = array_merge($completeClassInits, $rankClassInits); } $completeClassEntries = array(); foreach ($classEntries as $rankClassEntries) { asort($rankClassEntries, SORT_STRING); $completeClassEntries = array_merge($completeClassEntries, $rankClassEntries); } /** * Round 3. Process extension globals */ list($globalCode, $globalStruct, $globalsDefault) = $this->processExtensionGlobals($project); if ($project == 'zend') { $safeProject = 'zend_'; } else { $safeProject = $project; } /** * Round 4. Process extension info */ $phpInfo = $this->processExtensionInfo(); $toReplace = array('%PROJECT_LOWER_SAFE%' => strtolower($safeProject), '%PROJECT_LOWER%' => strtolower($project), '%PROJECT_UPPER%' => strtoupper($project), '%PROJECT_CAMELIZE%' => ucfirst($project), '%CLASS_ENTRIES%' => implode(PHP_EOL, array_merge($completeInterfaceEntries, $completeClassEntries)), '%CLASS_INITS%' => implode(PHP_EOL . "\t", array_merge($completeInterfaceInits, $completeClassInits)), '%INIT_GLOBALS%' => $globalsDefault, '%EXTENSION_INFO%' => $phpInfo); foreach ($toReplace as $mark => $replace) { $content = str_replace($mark, $replace, $content); } /** * Round 5. Generate and place the entry point of the project */ Utils::checkAndWriteIfNeeded($content, 'ext/' . $safeProject . '.c'); unset($content); /** * Round 6. Generate the project main header */ $content = file_get_contents(__DIR__ . '/../templates/project.h'); if (empty($content)) { throw new Exception("Template project.h doesn't exists"); } $includeHeaders = array(); foreach ($this->compiledFiles as $file) { if ($file) { $fileH = str_replace(".c", ".zep.h", $file); $include = '#include "' . $fileH . '"'; $includeHeaders[] = $include; } } $toReplace = array('%INCLUDE_HEADERS%' => implode(PHP_EOL, $includeHeaders)); foreach ($toReplace as $mark => $replace) { $content = str_replace($mark, $replace, $content); } Utils::checkAndWriteIfNeeded($content, 'ext/' . $safeProject . '.h'); unset($content); /** * Round 7. Create php_project.h */ $content = file_get_contents(__DIR__ . '/../templates/php_project.h'); if (empty($content)) { throw new Exception('Template php_project.h doesn`t exist'); } $toReplace = array('%PROJECT_LOWER_SAFE%' => strtolower($safeProject), '%PROJECT_LOWER%' => strtolower($project), '%PROJECT_UPPER%' => strtoupper($project), '%PROJECT_EXTNAME%' => strtolower($project), '%PROJECT_NAME%' => utf8_decode($this->config->get('name')), '%PROJECT_AUTHOR%' => utf8_decode($this->config->get('author')), '%PROJECT_VERSION%' => utf8_decode($this->config->get('version')), '%PROJECT_DESCRIPTION%' => utf8_decode($this->config->get('description')), '%PROJECT_ZEPVERSION%' => self::VERSION, '%EXTENSION_GLOBALS%' => $globalCode, '%EXTENSION_STRUCT_GLOBALS%' => $globalStruct); foreach ($toReplace as $mark => $replace) { $content = str_replace($mark, $replace, $content); } Utils::checkAndWriteIfNeeded($content, 'ext/php_' . $safeProject . '.h'); unset($content); return $needConfigure; }
/** * Generates the concatenation code * * @return array */ public function genConcatCode() { $code = ' #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <php.h> #include "php_ext.h" #include <ext/standard/php_string.h> #include "ext.h" #include "kernel/main.h" #include "kernel/memory.h" #include "kernel/concat.h"' . PHP_EOL . PHP_EOL; $pcodeh = ' #ifndef ZEPHIR_KERNEL_CONCAT_H #define ZEPHIR_KERNEL_CONCAT_H #include <php.h> #include <Zend/zend.h> #include "kernel/main.h" '; $codeh = ''; $macros = array(); ksort($this->concatKeys, SORT_STRING); foreach ($this->concatKeys as $key => $one) { $len = strlen($key); $params = array(); $zvalCopy = array(); $useCopy = array(); $avars = array(); $zvars = array(); $svars = array(); $lengths = array(); $sparams = array(); $lparams = array(); for ($i = 0; $i < $len; $i++) { $n = $i + 1; $t = substr($key, $i, 1); $sparams[] = 'op' . $n; if ($t == 's') { $params[] = 'const char *op' . $n . ', zend_uint op' . $n . '_len'; $lparams[] = 'op' . $n . ', sizeof(op' . $n . ')-1'; $lengths[] = 'op' . $n . '_len'; $svars[] = $n; $avars[$n] = 's'; } else { $params[] = 'zval *op' . $n; $lparams[] = 'op' . $n; $zvalCopy[] = 'op' . $n . '_copy'; $useCopy[] = 'use_copy' . $n . ' = 0'; $lengths[] = 'Z_STRLEN_P(op' . $n . ')'; $zvars[] = $n; $avars[$n] = 'v'; } } $macros[] = '#define ZEPHIR_CONCAT_' . strtoupper($key) . '(result, ' . join(', ', $sparams) . ') \\' . PHP_EOL . "\t" . ' zephir_concat_' . $key . '(result, ' . join(', ', $lparams) . ', 0);'; $macros[] = '#define ZEPHIR_SCONCAT_' . strtoupper($key) . '(result, ' . join(', ', $sparams) . ') \\' . PHP_EOL . "\t" . ' zephir_concat_' . $key . '(result, ' . join(', ', $lparams) . ', 1);'; $macros[] = ''; $proto = 'void zephir_concat_' . $key . '(zval *result, ' . join(', ', $params) . ', int self_var)'; $proto = 'void zephir_concat_' . $key . '(zval *result, ' . join(', ', $params) . ', int self_var)'; $codeh .= '' . $proto . ';' . PHP_EOL; $code .= $proto . '{' . PHP_EOL . PHP_EOL; if (count($zvalCopy)) { $code .= "\t" . 'zval result_copy, ' . join(', ', $zvalCopy) . ';' . PHP_EOL; $code .= "\t" . 'int use_copy = 0, ' . join(', ', $useCopy) . ';' . PHP_EOL; } else { $code .= "\t" . 'zval result_copy;' . PHP_EOL; $code .= "\t" . 'int use_copy = 0;' . PHP_EOL; } $code .= "\t" . 'uint offset = 0, length;' . PHP_EOL . PHP_EOL; foreach ($zvars as $zvar) { $code .= "\t" . 'if (Z_TYPE_P(op' . $zvar . ') != IS_STRING) {' . PHP_EOL; $code .= "\t" . ' use_copy' . $zvar . ' = zend_make_printable_zval(op' . $zvar . ', &op' . $zvar . '_copy);' . PHP_EOL; $code .= "\t" . ' if (use_copy' . $zvar . ') {' . PHP_EOL; $code .= "\t" . ' op' . $zvar . ' = &op' . $zvar . '_copy;' . PHP_EOL; $code .= "\t" . ' }' . PHP_EOL; $code .= "\t" . '}' . PHP_EOL . PHP_EOL; } $code .= "\t" . 'length = ' . join(' + ', $lengths) . ';' . PHP_EOL; $code .= "\t" . 'if (self_var) {' . PHP_EOL; $code .= '' . PHP_EOL; $code .= "\t\t" . 'if (Z_TYPE_P(result) != IS_STRING) {' . PHP_EOL; $code .= "\t\t\t" . 'use_copy = zend_make_printable_zval(result, &result_copy);' . PHP_EOL; $code .= "\t\t\t" . 'if (use_copy) {' . PHP_EOL; $code .= "\t\t\t\t" . 'ZEPHIR_CPY_WRT_CTOR(result, (&result_copy));' . PHP_EOL; $code .= "\t\t\t" . '}' . PHP_EOL; $code .= "\t\t" . '}' . PHP_EOL . PHP_EOL; $code .= "\t\t" . 'offset = Z_STRLEN_P(result);' . PHP_EOL; $code .= "\t\t" . 'length += offset;' . PHP_EOL; $code .= "\t\t" . 'Z_STR_P(result) = zend_string_realloc(Z_STR_P(result), length, 0);' . PHP_EOL; $code .= '' . PHP_EOL; $code .= "\t" . '} else {' . PHP_EOL; $code .= "\t\t" . 'ZVAL_STR(result, zend_string_alloc(length, 0));' . PHP_EOL; $code .= "\t" . '}' . PHP_EOL . PHP_EOL; $position = ''; foreach ($avars as $n => $type) { if ($type == 's') { $code .= "\t" . 'memcpy(Z_STRVAL_P(result) + offset' . $position . ', op' . $n . ', op' . $n . '_len);' . PHP_EOL; $position .= ' + op' . $n . '_len'; } else { $code .= "\t" . 'memcpy(Z_STRVAL_P(result) + offset' . $position . ', Z_STRVAL_P(op' . $n . '), Z_STRLEN_P(op' . $n . '));' . PHP_EOL; $position .= ' + Z_STRLEN_P(op' . $n . ')'; } } $code .= "\t" . 'Z_STRVAL_P(result)[length] = 0;' . PHP_EOL; $code .= "\t" . 'zend_string_forget_hash_val(Z_STR_P(result));' . PHP_EOL; foreach ($zvars as $zvar) { $code .= "\t" . 'if (use_copy' . $zvar . ') {' . PHP_EOL; $code .= "\t" . ' zval_dtor(op' . $zvar . ');' . PHP_EOL; $code .= "\t" . '}' . PHP_EOL . PHP_EOL; } $code .= "\t" . 'if (use_copy) {' . PHP_EOL; $code .= "\t" . ' zval_dtor(&result_copy);' . PHP_EOL; $code .= "\t" . '}' . PHP_EOL . PHP_EOL; $code .= "}" . PHP_EOL . PHP_EOL; } $code .= <<<EOF void zephir_concat_function(zval *result, zval *op1, zval *op2) /* {{{ */ { zval tmp; /* Force separate zval, if op2=result will be reallocated */ if (unlikely(result == op2)) { ZVAL_COPY_VALUE(&tmp, op2); op2 = &tmp; } concat_function(result, op1, op2 TSRMLS_CC); } EOF; $codeh .= "void zephir_concat_function(zval *result, zval *op1, zval *op2);\n#endif /* ZEPHIR_KERNEL_CONCAT_H */\n"; Utils::checkAndWriteIfNeeded($pcodeh . join(PHP_EOL, $macros) . PHP_EOL . PHP_EOL . $codeh, 'ext/kernel/concat.h'); Utils::checkAndWriteIfNeeded($code, 'ext/kernel/concat.c'); }
/** * Generates the concatenation code * * @return array */ public function genConcatCode() { $code = ' #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <php.h> #include "php_ext.h" #include <ext/standard/php_string.h> #include "ext.h" #include "kernel/main.h" #include "kernel/memory.h" #include "kernel/concat.h"' . PHP_EOL . PHP_EOL; $pcodeh = ' #ifndef ZEPHIR_KERNEL_CONCAT_H #define ZEPHIR_KERNEL_CONCAT_H #include <php.h> #include <Zend/zend.h> #include "kernel/main.h" '; $codeh = ''; $macros = array(); ksort($this->concatKeys, SORT_STRING); foreach ($this->concatKeys as $key => $one) { $len = strlen($key); $params = array(); $zvalCopy = array(); $useCopy = array(); $avars = array(); $zvars = array(); $svars = array(); $lengths = array(); $sparams = array(); $lparams = array(); for ($i = 0; $i < $len; $i++) { $n = $i + 1; $t = substr($key, $i, 1); $sparams[] = 'op' . $n; if ($t == 's') { $params[] = 'const char *op' . $n . ', zend_uint op' . $n . '_len'; $lparams[] = 'op' . $n . ', sizeof(op' . $n . ')-1'; $lengths[] = 'op' . $n . '_len'; $svars[] = $n; $avars[$n] = 's'; } else { $params[] = 'zval *op' . $n; $lparams[] = 'op' . $n; $zvalCopy[] = 'op' . $n . '_copy'; $useCopy[] = 'use_copy' . $n . ' = 0'; $lengths[] = 'Z_STRLEN_P(op' . $n . ')'; $zvars[] = $n; $avars[$n] = 'v'; } } $macros[] = '#define ZEPHIR_CONCAT_' . strtoupper($key) . '(result, ' . join(', ', $sparams) . ') \\' . PHP_EOL . "\t" . ' zephir_concat_' . $key . '(&result, ' . join(', ', $lparams) . ', 0 TSRMLS_CC);'; $macros[] = '#define ZEPHIR_SCONCAT_' . strtoupper($key) . '(result, ' . join(', ', $sparams) . ') \\' . PHP_EOL . "\t" . ' zephir_concat_' . $key . '(&result, ' . join(', ', $lparams) . ', 1 TSRMLS_CC);'; $macros[] = ''; $proto = 'void zephir_concat_' . $key . '(zval **result, ' . join(', ', $params) . ', int self_var TSRMLS_DC)'; $proto = 'void zephir_concat_' . $key . '(zval **result, ' . join(', ', $params) . ', int self_var TSRMLS_DC)'; $codeh .= '' . $proto . ';' . PHP_EOL; $code .= $proto . '{' . PHP_EOL . PHP_EOL; if (count($zvalCopy)) { $code .= "\t" . 'zval result_copy, ' . join(', ', $zvalCopy) . ';' . PHP_EOL; $code .= "\t" . 'int use_copy = 0, ' . join(', ', $useCopy) . ';' . PHP_EOL; } else { $code .= "\t" . 'zval result_copy;' . PHP_EOL; $code .= "\t" . 'int use_copy = 0;' . PHP_EOL; } $code .= "\t" . 'uint offset = 0, length;' . PHP_EOL . PHP_EOL; foreach ($zvars as $zvar) { $code .= "\t" . 'if (Z_TYPE_P(op' . $zvar . ') != IS_STRING) {' . PHP_EOL; $code .= "\t" . ' zend_make_printable_zval(op' . $zvar . ', &op' . $zvar . '_copy, &use_copy' . $zvar . ');' . PHP_EOL; $code .= "\t" . ' if (use_copy' . $zvar . ') {' . PHP_EOL; $code .= "\t" . ' op' . $zvar . ' = &op' . $zvar . '_copy;' . PHP_EOL; $code .= "\t" . ' }' . PHP_EOL; $code .= "\t" . '}' . PHP_EOL . PHP_EOL; } $code .= "\t" . 'length = ' . join(' + ', $lengths) . ';' . PHP_EOL; $code .= "\t" . 'if (self_var) {' . PHP_EOL; $code .= '' . PHP_EOL; $code .= "\t\t" . 'if (Z_TYPE_PP(result) != IS_STRING) {' . PHP_EOL; $code .= "\t\t\t" . 'zend_make_printable_zval(*result, &result_copy, &use_copy);' . PHP_EOL; $code .= "\t\t\t" . 'if (use_copy) {' . PHP_EOL; $code .= "\t\t\t\t" . 'ZEPHIR_CPY_WRT_CTOR(*result, (&result_copy));' . PHP_EOL; $code .= "\t\t\t" . '}' . PHP_EOL; $code .= "\t\t" . '}' . PHP_EOL . PHP_EOL; $code .= "\t\t" . 'offset = Z_STRLEN_PP(result);' . PHP_EOL; $code .= "\t\t" . 'length += offset;' . PHP_EOL; $code .= "\t\t" . 'Z_STRVAL_PP(result) = (char *) str_erealloc(Z_STRVAL_PP(result), length + 1);' . PHP_EOL; $code .= '' . PHP_EOL; $code .= "\t" . '} else {' . PHP_EOL; $code .= "\t\t" . 'Z_STRVAL_PP(result) = (char *) emalloc(length + 1);' . PHP_EOL; $code .= "\t" . '}' . PHP_EOL . PHP_EOL; $position = ''; foreach ($avars as $n => $type) { if ($type == 's') { $code .= "\t" . 'memcpy(Z_STRVAL_PP(result) + offset' . $position . ', op' . $n . ', op' . $n . '_len);' . PHP_EOL; $position .= ' + op' . $n . '_len'; } else { $code .= "\t" . 'memcpy(Z_STRVAL_PP(result) + offset' . $position . ', Z_STRVAL_P(op' . $n . '), Z_STRLEN_P(op' . $n . '));' . PHP_EOL; $position .= ' + Z_STRLEN_P(op' . $n . ')'; } } $code .= "\t" . 'Z_STRVAL_PP(result)[length] = 0;' . PHP_EOL; $code .= "\t" . 'Z_TYPE_PP(result) = IS_STRING;' . PHP_EOL; $code .= "\t" . 'Z_STRLEN_PP(result) = length;' . PHP_EOL . PHP_EOL; foreach ($zvars as $zvar) { $code .= "\t" . 'if (use_copy' . $zvar . ') {' . PHP_EOL; $code .= "\t" . ' zval_dtor(op' . $zvar . ');' . PHP_EOL; $code .= "\t" . '}' . PHP_EOL . PHP_EOL; } $code .= "\t" . 'if (use_copy) {' . PHP_EOL; $code .= "\t" . ' zval_dtor(&result_copy);' . PHP_EOL; $code .= "\t" . '}' . PHP_EOL . PHP_EOL; $code .= "}" . PHP_EOL . PHP_EOL; } $code .= <<<EOF void zephir_concat_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */ { #if PHP_VERSION_ID < 50400 \tzval op1_copy, op2_copy; \tint use_copy1 = 0, use_copy2 = 0; \tif (Z_TYPE_P(op1) != IS_STRING) { \t\tzend_make_printable_zval(op1, &op1_copy, &use_copy1); \t} \tif (Z_TYPE_P(op2) != IS_STRING) { \t\tzend_make_printable_zval(op2, &op2_copy, &use_copy2); \t} \tif (use_copy1) { \t\t/* We have created a converted copy of op1. Therefore, op1 won't become the result so \t\t * we have to free it. \t\t */ \t\tif (result == op1) { \t\t\tzval_dtor(op1); \t\t} \t\top1 = &op1_copy; \t} \tif (use_copy2) { \t\top2 = &op2_copy; \t} \tif (result == op1 && !IS_INTERNED(Z_STRVAL_P(op1))) {\t/* special case, perform operations on result */ \t\tuint res_len = Z_STRLEN_P(op1) + Z_STRLEN_P(op2); \t\tif (Z_STRLEN_P(result) < 0 || (int) (Z_STRLEN_P(op1) + Z_STRLEN_P(op2)) < 0) { \t\t\tstr_efree(Z_STRVAL_P(result)); \t\t\tZVAL_EMPTY_STRING(result); \t\t\tzend_error(E_ERROR, "String size overflow"); \t\t} \t\tZ_STRVAL_P(result) = str_erealloc(Z_STRVAL_P(result), res_len+1); \t\tmemcpy(Z_STRVAL_P(result) + Z_STRLEN_P(result), Z_STRVAL_P(op2), Z_STRLEN_P(op2)); \t\tZ_STRVAL_P(result)[res_len] = 0; \t\tZ_STRLEN_P(result) = res_len; \t} else { \t\tint length = Z_STRLEN_P(op1) + Z_STRLEN_P(op2); \t\tchar *buf = (char *) emalloc(length + 1); \t\tmemcpy(buf, Z_STRVAL_P(op1), Z_STRLEN_P(op1)); \t\tmemcpy(buf + Z_STRLEN_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op2)); \t\tbuf[length] = 0; \t\tZVAL_STRINGL(result, buf, length, 0); \t} \tif (use_copy1) { \t\tzval_dtor(op1); \t} \tif (use_copy2) { \t\tzval_dtor(op2); \t} #else concat_function(result, op1, op2 TSRMLS_CC); #endif } EOF; $codeh .= "void zephir_concat_function(zval *result, zval *op1, zval *op2 TSRMLS_DC);\n\n#endif /* ZEPHIR_KERNEL_CONCAT_H */\n\n"; Utils::checkAndWriteIfNeeded($pcodeh . join(PHP_EOL, $macros) . PHP_EOL . PHP_EOL . $codeh, 'ext/kernel/concat.h'); Utils::checkAndWriteIfNeeded($code, 'ext/kernel/concat.c'); }