Some php code improvement

Topics: Developer Forum
Jan 25, 2007 at 9:53 AM
Hello guys, I've viewed your php code and I thinks it's not much optimized for this simple cool idea.

Here You can view two revisited version of each class that should increase performance and make code more efficient.
I've not tested any part of this code but I suppose if there's some error it's simple to solve.

Best regards,
Andrea Giammarchi


code
<?php

  1. Andrea Giammarchi 2007/01/25
  2. You have the same check on MSAjaxProxyGenerator.php file too ... don't You think
  3. just one check is enought ?
if (strpos($_SERVER'REQUEST_URI', _FILE_) !== FALSE)
exit;

  1. Andrea Giammarchi 2007/01/25
  2. I think require should be enought ... however, any change
  3. However, this class depends on MSAjaxProxyGenerator ... output generation has been moved there
require_once 'MSAjaxProxyGenerator.php';

class MSAjaxService
{
# This feels like a hack... but it seems to work. :-)
# If anyone has a better solution of how to cast to an arbitrary class, please contribute it!
# Andrea Giammarchi 2007/01/25
# a bit faster with ternary operator and modfied substr (You need first ":" of serialized array too)
# I suppose a null value should be a better response (false should be ambigous)
function typecast($oldobject, $newclassname) {
return classexists($newclassname) ?
'O:'.strlen($newclassname).'"'.$newclassname.'"'.substr(serialize($old_object), 1) : false
}

# Andrea Giammarchi 2007/01/25
# few optimizzations, less redundance code ... faster
function processRequest() {
try {
$path = &$_SERVER'REQUEST_URI';
if (substr($path, strlen($path) - 3) === '/js')
calluserfunc(array(new MSAjaxProxyGenerator, 'generateClientProxy'), $this);
else {
$methodName = substr($path, strrpos($path, '/') + 1);
$reflect = new ReflectionObject($this);
$method = $reflect->hasMethod($methodName) ? $reflect->getMethod($methodName) : null;
$ctype = isset($SERVER'HTTP_CONTENT_TYPE') ? 'HTTPCONTENTTYPE' : 'CONTENTTYPE';
if(is_null($method) || $method->isStatic() || !$method->isPublic() || $method->getDeclaringClass()->getName() === 'MSAjaxService'))
throw new Exception('Method ' . $methodName . ' not found');
if($_SERVER'REQUEST_METHOD' !== 'POST')
throw new Exception('Wrong HTTP method, should be POST');
if(strpos($_SERVER$ctype, 'application/json') !== 0)
throw new Exception('Wrong content-type, should be application/json');
$args = array();
if(strlen($postData = trim(filegetcontents('php://input')))) {
$argsAsParams = json_decode($postData, true);
foreach($method->getParameters() as $i => &$param)
$args$i = $param->getClass() ?
$this->typecast(
$argsAsParams$param->getName(),
$param->getClass()->getName()
) : $argsAsParams$param->getName();
}
# I suppose this class should produce everytime a new output, use false otherwise
calluserfunc(array(new MSAjaxProxyGenerator, 'newJsonContent'), $method->invokeArgs($this, $args), true);
}
}
catch(Exception $e) {
header('HTTP/1.0 500 Internal Server Error');
# I suppose this class should produce everytime a new output, use false otherwise
calluserfunc(array(new MSAjaxProxyGenerator, 'newJsonContent'), $this->newJsonContent(array(
'Message' => $e->getMessage(),
'ExceptionType' => get_class($e),
'StackTrace' => $e->getTraceAsString()
)), true);
}
}
}

?>
/code

code
<?php

  1. Andrea Giammarchi 2007/01/25
  2. You have the same check on MSAjaxProxyGenerator.php file too ... don't You think
  3. just one check is enought ?
if (strpos($_SERVER'REQUEST_URI', _FILE_) !== FALSE)
exit;

class MSAjaxProxyGenerator
{

# Andrea Giammarchi 2007/01/25
# no cache and json encoding (think about Pear alternative for PHP < 5.2)
# example: http://www.devpro.it/code/140.html
# alternative: http://www.phpclasses.org/browse/file/17166.html
function newJsonContent($returnValue, $newOutput = true){
$returnValue = json_encode($returnValue);
header('Content-Type: application/json');
header('Content-Length: '.strlen($returnValue));
if($newOutput) {
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
}
echo $returnValue;
}

# Andrea Giammarchi 2007/01/25
# make code more efficient, write less having more control or simple mantainment ;-)
private function _validmethod(&$method){
return !$method->isStatic() && $method->isPublic() && $method->getDeclaringClass()->getName() !== 'MSAjaxService';
}

# Andrea Giammarchi 2007/01/25
# few performance improvements, less redundance, better code ? I hope
# P.S. echo is slow (print is even slower) ... don't write 6238737172163 in your scripts if You care about performances
function generateClientProxy($obj) {
$applicationRoot = dirname($_SERVER'SCRIPT_NAME');
$reflect = new ReflectionObject($obj);
$classname = $reflect->getName();
$line = "\r\n";
$output = array(
'var ', $classname, '=function(){', $line,
$classname, '.initializeBase(this);', $line,
'this.timeout=0;this.userContext=null;this.succeeded=null;this.failed=null;', $line
'};', $line, $classname, 'prototype={', $line
);
$second = 0;
foreach ($reflect->getMethods() as $m => &$method) {
if($this->_validmethod($method)) {
if($second == 1)
$output[] = ','.$line;
$output[] = $method->getName().':function('
foreach ($method->getParameters() as $p => &$param)
$output[] = $param->getName().',';
$output[] = 'succeededCallback,failedCallback,userContext){'.$line.
'return this.invoke('.$classname.'.getpath(),"'.$method->getName().'",false,{';
$second = 0;
foreach ($method->getParameters() as $p => &$param) {
if($second == 1)
$output[] = ',';
$output[] = $param->getName().':'.$param->getName();
$second = 1;
}
$output[] = '},succeededCallback,failedCallback,userContext);}';
$second = 1;
}
}
array_push($output, '};', $line,
$classname, '.registerClass("', $classname, '",Sys.Net.WebServiceProxy);', $line,
$classname, '._staticInstance=new ', $classname, ';', $line,
$classname, '.setpath=function(value){', $classname, '.staticInstance._path=value};', $line,
$classname, '.getpath=function(){return ', $classname, '.staticInstance._path};', $line,
$classname, '.settimeout=function(value){', $classname, '.staticInstance._timeout=value};', $line,
$classname, '.gettimeout=function(){return ', $classname, '.staticInstance._timeout};', $line,
$classname, '.setdefaultUserContext=function(value){', $classname, '.staticInstance._userContext=value};', $line,
$classname, '.getdefaultUserContext=function(){return ', $classname, '.staticInstance._userContext};', $line,
$classname, '.setdefaultSucceededCallback=function(value){', $classname, '.staticInstance._succeeded=value};', $line,
$classname, '.getdefaultSucceededCallback=function(){return ', $classname, '.staticInstance._succeeded};', $line,
$classname, '.setdefaultFailedCallback=function(value){', $classname, '.staticInstance._failed=value};', $line,
$classname, '.getdefaultFailedCallback=function(){return ', $classname, '.staticInstance._failed};', $line
);
$pathWithoutProxy = $_SERVER'REQUEST_URI';
$pathWithoutProxy = substr($pathWithoutProxy, 0, strlen($pathWithoutProxy) - 3);
$pathWithoutProxy = addslashes($pathWithoutProxy);
$output[] = $classname.'.set_path("'.$pathWithoutProxy.'");'.$line;
foreach ($reflect->getMethods() as $m => &$method) {
if ($this->_validmethod($method)) {
$output[] = $classname.'.'.$method->getName().'=function(';
$params = '';
foreach($method->getParameters() as $p => $param)
$params .= $param->getName() . ',';
arraypush($output, $params, 'onSuccess,onFailed,userContext){', $classname, '.staticInstance.', $method->getName(), '(', $params, 'onSuccess,onFailed,userContext)};', $line);
# use false if You don't need a new layout cached classes
$this->newJsonContent(implode('', $output), true);
}
}
}
}
?>
/code
Jan 25, 2007 at 9:55 AM
damn! ... I suppose this link should be better: http://www.3site.eu/freedownload/dist.zip

Regards :-)
Coordinator
Jan 26, 2007 at 5:07 AM
Wow, thank you very much!

I'm a pro at the Microsoft AJAX Library, but a novice at PHP, so this kind of help is greatly appreciated.

Skimming the changes you propose, they look great. I'll take a deeper look over the next few days when I get a chance and incorporate them into the next release.

Thanks again for taking the time to help. If you're interested in being more involved in the project, email me (Steve.Marx@microsoft.com). I could always use more help on the PHP side of things.
Feb 13, 2007 at 9:31 AM
PHP uses copy-on-write, so code $a = $b; doesn't copy any memory. Thus code $a = &$b; is in fact slower than $a = $b;. Enlightment is: Don't use reference assignments where not necessary.

PHP construct echo is very fast. Several Response.Write were slow in ASP but it doesn't hold for PHP. Code $a = ""; $a .= "..."; echo $a; is similarly fast, but $a = array(); $a[] = "..."; echo implode("", $a); is in fact slower because it allocates more than double of memory needed for output (first for array, second for imploded string + overhead costs for array manipulation).