Jump to content

Archived

This topic is now archived and is closed to further replies.

daeken

PHP Extension Code Generator

Recommended Posts

Well, after about half an hour of work, I have a full rewrite of my skeleton generator. This time, instead of a C header file, you create a function definition file. Example:


int foo(string $bar, int $bleh); Simple function!

bool is_even(mixed $var); Checks if a variable is even.

 

Another notable difference is that it generates full documentation in the C code, and parses parameters. Example output of that above definition file is:


function_entry openal_functions[] = {

 ZEND_FE(foo, NULL)

 ZEND_FE(is_even, NULL)

 {NULL, NULL, NULL}

};





/* {{{ proto int foo(string bar, int bleh)

  Simple function! */

PHP_FUNCTION(foo)

{

 char *bar;

 int bar_len;

 int bleh;

 if(ZEND_NUM_ARGS() < 2) WRONG_PARAM_COUNT;

 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &bar, &bar_len, &bleh) == FAILURE)

   return;

}



/* {{{ proto bool is_even(mixed var)

  Checks if a variable is even. */

PHP_FUNCTION(is_even)

{

 zval *var;

 if(ZEND_NUM_ARGS() < 1) WRONG_PARAM_COUNT;

 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &var) == FAILURE)

   return;

}

 

As you can see, this does all the really hard work for you, leading to more efficient extension building :)

 

And now for the code...

[php:1:7617edc449]

#!/usr/local/bin/php

<?php

if($_SERVER[\'argc\'] < 3)

{

echo \'Usage: \', $_SERVER[\'argv\'][0], \' <extension name> <definition>\', chr(10);

exit;

}

$file = file($_SERVER[\'argv\'][2]);

$defs = array();

foreach($file as $line)

{

$parts = explode(\' \', trim($line));

$arr = array();

$arr[\'return\'] = $parts[0];

$arr[\'name\'] = substr($parts[1], 0, strpos($parts[1], \'(\'));

$arr[\'params\'] = array();

$arr[\'desc\'] = trim(substr($line, strpos($line, \';\') + 1));

$pos = strpos($line, \'(\') + 1;

$line = substr($line, $pos, strrpos($line, \')\') - $pos);

$params = explode(\',\', $line);

foreach($params as $part)

{

list($type, $name) = explode(\' \', trim($part));

$arr[\'params\'][str_replace(\'$\', (string) null, $name)] = $type;

}

$defs[] = $arr;

}

echo \'function_entry \', $_SERVER[\'argv\'][1], \'_functions[] = {\', chr(10);

foreach($defs as $arr)

echo \' ZEND_FE(\', $arr[\'name\'], \', NULL)\', chr(10);

echo \' {NULL, NULL, NULL}\', chr(10), \'};\', chr(10), chr(10), chr(10);

foreach($defs as $arr)

{

echo \'/* {{{ proto \', $arr[\'return\'], \' \', $arr[\'name\'], \'(\';

$i = 0;

foreach($arr[\'params\'] as $name => $type)

{

echo $type, \' \', $name;

if(++$i != count($arr[\'params\']))

echo \', \';

}

echo \')\', chr(10), \' \', $arr[\'desc\'], \' */\', chr(10), \'PHP_FUNCTION(\', $arr[\'name\'], \')\', chr(10), \'{\', chr(10);

$format = (string) null;

$params = (string) null;

$i = 0;

foreach($arr[\'params\'] as $name => $type)

{

switch($type)

{

case \'int\':

echo \' int \', $name, \';\', chr(10);

$format .= \'l\';

$params .= \'&\' . $name;

break;

case \'string\':

echo \' char *\', $name, \';\', chr(10), \' int \', $name, \'_len;\', chr(10);

$format .= \'s\';

$params .= \'&\' . $name . \', &\' . $name . \'_len\';

break;

case \'bool\':

case \'boolean\':

echo \' zend_bool \', $name, \';\', chr(10);

$format .= \'b\';

$params .= \'&\' . $name;

break;

case \'array\':

echo \' zval *\', $name, \';\', chr(10);

$format .= \'a\';

$params .= \'&\' . $name;

break;

case \'mixed\':

case \'resource\':

echo \' zval *\', $name, \';\', chr(10);

$format .= \'z\';

$params .= \'&\' . $name;

break;

case \'object\':

echo \' zval *\', $name, \';\', chr(10);

$format .= \'o\';

$params .= \'&\' . $name;

break;

case \'double\':

case \'float\':

echo \' double \', $name, \';\', chr(10);

$format .= \'d\';

$params .= \'&\' . $name;

break;

}

if(++$i != count($arr[\'params\']))

$params .= \', \';

}

echo \' if(ZEND_NUM_ARGS() < \', count($arr[\'params\']), \') WRONG_PARAM_COUNT;\', chr(10), \' if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, \"\', $format, \'\", \', $params, \') == FAILURE)\', chr(10), \' return;\', chr(10), \'}\', chr(10), chr(10);

}

?>

[/php:1:7617edc449]

 

Have fun :)

 

Happy hacking,

Lord Daeken M. BlackBlade

(Cody Brocious)

Share this post


Link to post
Share on other sites

great! this will cut the time of developing extensions by 1/4, atleast :)

 

idea:

 


int foo(string $bar, int $bleh[, string my_optional_var]);

Share this post


Link to post
Share on other sites

New version, new parser, and a new feature: optional parameters.

 

#!/usr/local/bin/php

<?php

if($_SERVER[\'argc\'] < 3)

{

 echo \'Usage: \', $_SERVER[\'argv\'][0], \' <extension name> <definition>\', chr(10);

 exit;

}

$file = file($_SERVER[\'argv\'][2]);

$defs = array();

foreach($file as $line)

{

 $parts = explode(\' \', trim($line));

 $arr = array();

 $arr[\'return\'] = $parts[0];

 $arr[\'name\'] = substr($parts[1], 0, strpos($parts[1], \'(\'));

 $arr[\'params\'] = array();

 $arr[\'desc\'] = trim(substr($line, strpos($line, \';\') + 1));

 $pos = strpos($line, \'(\') + 1;

 $line = trim(substr($line, $pos, strrpos($line, \')\') - $pos));

 $last = 0;

 $opt = false;

 preg_match_all(\'/([|])?[s]*(.+?)[s]+$([^s[],=]+)[s]*=?[s]*([^s[]]*)[s]*([|])*,?[s]*/\', $line, $matches);

 for($i = 0; $i < count($matches[0]); ++$i)

 {

   $type = $matches[2][$i];

   $name = $matches[3][$i];

   $def = $matches[4][$i];

   if($matches[1][$i] == \'[\')

     $opt = $tempopt = true;

   elseif($matches[5][$i] == \'[\')

     $tempopt = true;

   elseif($matches[5][$i] == \']\' || empty($matches[5][$i]))

     $tempopt = false;

   $arr[\'params\'][$name] = array($type, $opt, $def);

   $opt = $tempopt;

 }

 $defs[] = $arr;

}

echo \'function_entry \', $_SERVER[\'argv\'][1], \'_functions[] = {\', chr(10);

foreach($defs as $arr)

 echo \'  ZEND_FE(\', $arr[\'name\'], \', NULL)\', chr(10);

echo \'  {NULL, NULL, NULL}\', chr(10), \'};\', chr(10), chr(10), chr(10);

foreach($defs as $arr)

{

 echo \'/* {{{ proto \', $arr[\'return\'], \' \', $arr[\'name\'], \'(\';

 $i = 0;

 $optcount = 0;

 foreach($arr[\'params\'] as $name => $temp)

 {

   list($type, $opt, $default) = $temp;

   if(!$opt)

   {

     echo str_repeat(\']\', $optcount);

     $optcount = 0;

   }

   if($opt)

   {

     if($i != 0)

       echo \' \';

     echo \'[\';

     ++$optcount;

   }

   if(++$i > 1)

     echo \', \';

   echo $type, \' \', $name;

   if($opt && !empty($default))

     echo \' = \', $default;

 }

 echo str_repeat(\']\', $optcount);

 echo \')\', chr(10), \'   \', $arr[\'desc\'], \' */\', chr(10), \'PHP_FUNCTION(\', $arr[\'name\'], \')\', chr(10), \'{\', chr(10);

 $format = (string) null;

 $params = (string) null;

 $i = 0;

 foreach($arr[\'params\'] as $name => $temp)

 {

   list($type, $opt, $default) = $temp;

   switch($type)

   {

   case \'int\':

     echo \'  int \', $name;

     if($opt && !empty($default))

       echo \' = \', $default;

     elseif($opt)

       echo \' = 0\';

     echo \';\', chr(10);

     if($opt)

       $format .= \'|\';

     $format .= \'l\';

     $params .= \'&\' . $name;

     break;

   case \'string\':

     echo \'  char *\', $name;

     if($opt && !empty($default))

       echo \' = \', $default;

     elseif($opt)

       echo \' = NULL\';

     echo \';\', chr(10), \'  int \', $name, \'_len\';

     if($opt && !empty($default))

       echo \' = \', strlen($default) - 2;

     echo \';\', chr(10);

     if($opt)

       $format .= \'|\';

     $format .= \'s\';

     $params .= \'&\' . $name . \', &\' . $name . \'_len\';

     break;

   case \'bool\':

   case \'boolean\':

     echo \'  zend_bool \', $name;

     if($opt && !empty($default))

       echo \' = \', $default;

     elseif($opt)

       echo \' = FALSE\';

     echo \';\', chr(10);

     if($opt)

       $format .= \'|\';

     $format .= \'b\';

     $params .= \'&\' . $name;

     break;

   case \'array\':

     echo \'  zval *\', $name;

     if($opt && !empty($default))

       echo \' = \', $default;

     elseif($opt)

       echo \' = NULL\';

     echo \';\', chr(10);

     if($opt)

       $format .= \'|\';

     $format .= \'a\';

     $params .= \'&\' . $name;

     break;

   case \'mixed\':

   case \'resource\':

     echo \'  zval *\', $name;

     if($opt && !empty($default))

       echo \' = \', $default;

     elseif($opt)

       echo \' = NULL\';

     echo \';\', chr(10);

     if($opt)

       $format .= \'|\';

     $format .= \'z\';

     $params .= \'&\' . $name;

     break;

   case \'object\':

     echo \'  zval *\', $name;

     if($opt && !empty($default))

       echo \' = \', $default;

     elseif($opt)

       echo \' = NULL\';

     echo \';\', chr(10);

     if($opt)

       $format .= \'|\';

     $format .= \'o\';

     $params .= \'&\' . $name;

     break;

   case \'double\':

   case \'float\':

     echo \'  double \', $name;

     if($opt && !empty($default))

       echo \' = \', $default;

     elseif($opt)

       echo \' = 0\';

     echo \';\', chr(10);

     if($opt)

       $format .= \'|\';

     $format .= \'d\';

     $params .= \'&\' . $name;

     break;

   }

   if(++$i != count($arr[\'params\']))

     $params .= \', \';

 }

 $need = 0;

 foreach($arr[\'params\'] as $name => $temp)

 {

   if(!$temp[1])

     ++$need;

 }

 echo \'  if(ZEND_NUM_ARGS() < \', $need, \') WRONG_PARAM_COUNT;\', chr(10), \'  if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "\', $format, \'", \', $params, \') == FAILURE)\', chr(10), \'    return;\', chr(10), \'}\', chr(10), chr(10);

}

?>

 

Example definition:


int foo(string $bar[, int $bleh]); Simple function!

bool is_even([mixed $var = 5]); Checks if a variable is even.

 

Output:


function_entry openal_functions[] = {

 ZEND_FE(foo, NULL)

 ZEND_FE(is_even, NULL)

 {NULL, NULL, NULL}

};





/* {{{ proto int foo(string bar [, int bleh])

  Simple function! */

PHP_FUNCTION(foo)

{

 char *bar;

 int bar_len;

 int bleh = 0;

 if(ZEND_NUM_ARGS() < 1) WRONG_PARAM_COUNT;

 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &bar, &bar_len, &bleh) == FAILURE)

   return;

}



/* {{{ proto bool is_even([mixed var = 5])

  Checks if a variable is even. */

PHP_FUNCTION(is_even)

{

 zval *var = 5;

 if(ZEND_NUM_ARGS() < 0) WRONG_PARAM_COUNT;

 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z", &var) == FAILURE)

   return;

}

 

Have fun!

 

Happy hacking,

Lord Daeken M. BlackBlade

(Cody Brocious)

Share this post


Link to post
Share on other sites

..had to edit that and change to CODE tags.

 

phpBB\'s PHP tags destroy & and && operators :(

Share this post


Link to post
Share on other sites

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.