Jump to content

PHP Extension Code Generator


daeken

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)

Link to comment
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)

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

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.