To All:

But especially [url=http://shiflett.org/archive/180]Chris[/url]:

[php]

$constants = array(
'F/a' => 'mass', // http://en.wikipedia.org/wiki/Newton%27s_laws_of_motion#Newton.27s_second_law:_fundamental_law_of_dynamics
'783487' => 'y', // arbitrary number
's' => 'preg_replace', // perl )-:
2.71828 => 'e', // http://en.wikipedia.org/wiki/E_%28mathematical_constant%29
'x' => '278127', // arbitrary number
'278127' =>'Christ', // http://en.wikipedia.org/wiki/%CE%A7
'y' => '783487',
);

$formula = array(
'M',
2.71828,
'r', /* pow(r, 2) */
'r', /* = r*r = rr */
( pow((1/$constants['y']), -1) ),
"\n",
sqrt(pow($constants['x'], 2)),
$constants['F/a'], // force = F; acceleration = a
"\n",
's/ss/s'
);
$adjust = array_pop($formula);

$message = '';
foreach ($formula AS $p) {
if (isset($constants[$p])) {
$message .= $constants[$p];
} else {
$message .= $p;
}
}

list($func, $regex, $replace) = explode('/', $adjust);

$message = call_user_func($constants[$func],"/{$regex}/","{$replace}", $message);

echo $message;

?>[/php]

Java Guys in a Nutshell

If you've ever had the "pleasure" of working with hardcore-Java types, you'll likely chuckle at the following:
[quote]Maybe while the Java world was engaged in talking of high end, super techie stuff, with the words 'enterprise', 'transactions' and 'SOA 'embedded in every sentence, the PHP guys actually went out and created a lot of simple yet very useful software.[/quote]

(from: [url=http://indicthreads.com/blogs/161/Java_PHP_CMS_Comparison.html]http://indicthreads.com/blogs/161/Java_PHP_CMS_Comparison.html[/url])

S

Over-zealous Ebay ads

Well, definitely not PHP related, but...

Anyone else noticed that Ebay's affiliates have been a little over-zealous in their ads, lately?

Babies for sale...
[img]http://blog.phpdoc.info/uploads/adsgonewrong.png[/img]

Google for "bombs"...
[img]http://blog.phpdoc.info/uploads/adsgonewrong2.png[/img]

(my wife pointed out the "babies" one)

Weird stuff.. (-:

S

Fun with the tokenizer...

I was reminded, this past week, of how cool the [url=http://php.net/tokenizer]tokenizer[/url] is.

One of the guys who works in the same office as I do had what seemed to be a simple problem: he had a php file that contained ~50 functions, and wanted to summarize the API without parsing through the file, manually, and cutting out the function declarations.

We introduced him to [url=http://www.phpdoc.org/]in-line phpdoc blocks[/url] (he works (as a Jr.-level PHP developer) in the same office, but for a different company, so he doesn't have to follow our coding standards, but I digress..), but the 50-function library in question didn't have docblocks.

Sure, he could (and did) pull up a list function NAMES with [url=http://php.net/get_defined_functions]get_defined_functions[/url] (I assume by using [url=http://php.net/array_diff]array_diff[/url] against a before-and-after capture), but this didn't give him the argument names, or even the number of arguments for a given function, so I broke out some old tokenizer code I'd written.

In case you aren't familiar with the tokenizer, the PHP manual defines it as:
[quote][an interface to let you write] your own PHP source analyzing or modification tools without having to deal with the language specification at the lexical level.[/quote]

The extension (which has been part of the PHP core distribution since 4.3.0) consists only of two functions: [url=http://php.net/token_get_all]token_get_all[/url] and [url=http://php.net/token_name]token_name[/url], and a [url=http://php.net/tokens]boatload of constants[/url].

Enough babble, though, let's get to the meat. I pulled out this code I'd written for PEARClops (on EFNet #PEAR) that parses PHP source files and figures out what classes, functions/methods and associated parameters are included.

[php]

function get_protos($in)
{
if (is_file(realpath($in)))
{
$in = file_get_contents($in);
}
$tokens = token_get_all($in);
$funcs = array();
$currClass = '';
$classDepth = 0;

for ($i=0; $i {
if (is_array($tokens[$i]) && $tokens[$i][0] == T_CLASS)
{
++$i; // whitespace;
$currClass = $tokens[++$i][1];
while ($tokens[++$i] != '{') {}
++$i;
$classDepth = 1;
continue;
}
elseif (is_array($tokens[$i]) && $tokens[$i][0] == T_FUNCTION)
{
$nextByRef = FALSE;
$thisFunc = array();

while ($tokens[++$i] != ')')
{
if (is_array($tokens[$i]) && $tokens[$i][0] != T_WHITESPACE)
{
if (!$thisFunc)
{
$thisFunc = array(
'name' => $tokens[$i][1],
'class' => $currClass,
);
}
else
{
$thisFunc['params'][] = array(
'byRef' => $nextByRef,
'name' => $tokens[$i][1],
);
$nextByRef = FALSE;
}
}
elseif ($tokens[$i] == '&')
{
$nextByRef = TRUE;
}
elseif ($tokens[$i] == '=')
{
while (!in_array($tokens[++$i], array(')',',')))
{
if ($tokens[$i][0] != T_WHITESPACE)
{
break;
}
}
$thisFunc['params'][count($thisFunc['params']) - 1]['default'] = $tokens[$i][1];
}
}
$funcs[] = $thisFunc;
}
elseif ($tokens[$i] == '{')
{
++$classDepth;
}
elseif ($tokens[$i] == '}')
{
--$classDepth;
}

if ($classDepth == 0)
{
$currClass = '';
}
}

return $funcs;
}

function parse_protos($funcs)
{
$protos = array();
foreach ($funcs AS $funcData)
{
$proto = '';
if ($funcData['class'])
{
$proto .= $funcData['class'];
$proto .= '::';
}
$proto .= $funcData['name'];
$proto .= '(';
if ($funcData['params'])
{
$isFirst = TRUE;
foreach ($funcData['params'] AS $param)
{
if ($isFirst)
{
$isFirst = FALSE;
}
else
{
$proto .= ', ';
}

if ($param['byRef'])
{
$proto .= '&';
}
$proto .= $param['name'];
}
}
$proto .= ")";
$protos[] = $proto;
}

return $protos;
}

echo "Functions in {$_SERVER['argv'][1]}:\n";
foreach (parse_protos(get_protos($_SERVER['argv'][1])) AS $proto)
{
echo " $proto\n";
}

?>[/php]

Save it as "parse_funcs.php" (or whatever you like) and call it like so:
php parse_funcs.php /path/to/php_file

For instance:
[code]
sean@iconoclast:~/php/scripts$ php token_funcs_cli.php ~/php/cvs/Mail_Mime/mime.php
Functions in /home/sean/php/cvs/Mail_Mime/mime.php:
Mail_mime::Mail_mime($crlf)
Mail_mime::__wakeup()
Mail_mime::setTXTBody($data, $isfile, $append)
Mail_mime::setHTMLBody($data, $isfile)
Mail_mime::addHTMLImage($file, $c_type, $name, $isfilename)
Mail_mime::addAttachment($file, $c_type, $name, $isfilename, $encoding)
Mail_mime::_file2str(&$file_name)
Mail_mime::_addTextPart(&$obj, $text)
Mail_mime::_addHtmlPart(&$obj)
Mail_mime::_addMixedPart()
Mail_mime::_addAlternativePart(&$obj)
Mail_mime::_addRelatedPart(&$obj)
Mail_mime::_addHtmlImagePart(&$obj, $value)
Mail_mime::_addAttachmentPart(&$obj, $value)
Mail_mime::get(&$build_params)
Mail_mime::headers(&$xtra_headers)
Mail_mime::txtHeaders($xtra_headers)
Mail_mime::setSubject($subject)
Mail_mime::setFrom($email)
Mail_mime::addCc($email)
Mail_mime::addBcc($email)
Mail_mime::_encodeHeaders($input)
Mail_mime::_setEOL($eol)
[/code]

Not bad, huh?

There are some not-so-obvious bugs (inheritance, mostly), but for a relatively short script, it does a pretty good job.

S

PHP Fun - Variable Arguments Be Reference?

Earlier, this week, one of my co-workers was working on a personal project in which he wanted to use a function to set a variable number of parameters to zero.

[php]// he wanted this:
$a = 1; $b = 2; $c = 3;
set_to_zero($a, $b, $c);
echo "$a $b $c"; // prints "0 0 0";
?>[/php]

His first impulse was to use func_get_args(). But this wasn't working for him. Turns out function_get_args() returns a COPY of the arguments, and not a reference. The manual didn't lean either way or the other, so I updated it.

A few mails bounced around our internal developer's list. It seems that there's no non-hack way to do this.
Here's what we came up with:

[b]Non-Solution #1[/b]
[php]function set_array_to_zero(&$array)
{
foreach ($array AS $k => $v) {
$array[$k] = 0;
}
}
$a = 1; $b = 2; $c = 3;
set_array_to_zero(array(&$a, &$b, &$c));
echo "$a $b $c"; // prints "0 0 0";
?>[/php]
This WORKS, and is probably the most "proper" way to do this, but the semantics violate his original requirements.

A couple other non-semantic-repecting solutions were proposed (one using $obj = new StdClass;), but nothing that really worked the way he intended.

Here's one that seems pretty close, on the surface:
[b]Non-Solution #4[/b]
[php]function set_var_to_zero($var)
{
for ($i=0; $i $GLOBALS[func_get_arg($i)] = 0;
}
}
$a = 1; $b = 2; $c = 3;
set_var_to_zero('a', 'b', 'c');
echo "$a $b $c"; // prints "0 0 0";
?>[/php]

Sure, it breaks semantics, again, but there's another major problem -- scope:
[b]Non-Solution #5[/b]
[php]function set_var_to_zero($var)
{
for ($i=0; $i $GLOBALS[func_get_arg($i)] = 0;
}
}

function foo()
{
$a = 1; $b = 2; $c = 3;
set_var_to_zero('a', 'b', 'c');
echo "$a $b $c"; // actually prints "1 2 3";
}

$a = 4; $b = 5; $c = 6;
foo();
?>[/php]

Since there's no way to operate on an intermediate scope (only local symbols and global symbols), at least in a functional context, this approach is a dead end.

Finally, after sleeping on it, I came up with this:

[b]Non-Solution #16[/b]
[php]error_reporting(E_ALL);
define('STZ_MAX_ARGUMENTS', 1023);
$set_to_zero = create_function(
'&$a'. implode(',&$a', range(0, STZ_MAX_ARGUMENTS)),
'for ($i=0; $i <= STZ_MAX_ARGUMENTS; $i++) ${"a$i"} = 0;'
);
$a = 1; $b = 2; $c = 3;
@$set_to_zero($a, $b, $c);
echo "$a $b $c"; // prints "0 0 0";
?>[/php]

This ALMOST works -- I mean, it's REALLY close. The semantics are still a LITTLE off -- the $ I can live with. The @error_suppressor is also necessary, because it seems that it's not possible to pass default values to create_function(...) (try removing the @ -- you'll get (1024 - actual_number_of_arguments) error messages). There's also the issue of limiting the number of arguments to an arbitrary number. The way I see it, no developer should be working on > 1000 (actually, even that is entirely too many), anyway.

So, without resorting to eval / writing to a file + include, this is the best solution we could come up with.

Personally, I'd alter my requirements to go with the array-passing approach.

S

 1

About

User

You are logged in as Anonymous.

Want to log out?


Clicky Web Analytics