PDA

View Full Version : Ext.Direct PHP backend



ckr
9 May 2009, 1:19 PM
1/25/2010
Found a few bugs when working with Ext-3.1.0 (my mistakes, not Ext's). Updated the zip file. You should be able to unzip into your examples directory (in your Ext install), and run from there.

Biggest change was due to my incorrect handling of the formHandler value required by the API. So that the API generator can detect that a particular method is a form handler, you need to add frmHandler to your method name. You can see the comments in the Profile.php class file. There was really no way that I could see of getting that info using Reflections. Someone smarter than me may have a better idea here.

Remember the main goal (at least for me) was to simply write the php code, and then have the config and api generated from the classes, instead of having to manually create and maintain them.

Demo is working with 3.1 just fine, and the demo link below has been updated to the Ext-3.1.0 version.

---------------------------------------------------------------------------------
7/11/2009
Missed a variable name change in the before and after calls.
Fixed.

7/9/2009
Fixed router for multiple arguments in actions (finally). I misunderstood what was happening there. But this should be good to go now.

Zip has been updated.
Demo here (http://mynetvoid.com/directdemo3/direct-demo.php).
---------------------------------------------------------------------------------
6/17/09
Fixed router to better handle parameters
Update demo page (with form, and tree examples)

Updated the attachment with latest changes.
---------------------------------------------------------------------------------

6/5/09
Fixed router to handle parameters properly
Fixed utils to handle multiple server action classes

Updated the attachment with latest changes.

---------------------------------------------------------------------------------
Ext.Direct PHP backend
First I am not a developer, more of a guy that likes to hack around. So this is by no means and end all solution. In fact, it might just be drivel. But while working with the examples and attempting to see how I could do some comet long polling stuff, I have re-worked the PHP backend for Ext.Direct just a bit.


Might want to save your originals before unzipping.

ckr
27 May 2009, 6:27 PM
**Updated*** So that this is more functional (the previous post is blah)

First thing to note however, is if you need a really robust solution, I would suggest looking at Tommy's solution here (http://www.extjs.com/forum/showthread.php?p=328386#post328386). So maybe this should be viewed as a "Lite" implementation.

I was looking for something a little more lightweight and less involved. So I completely rewrote the previous approach. This now supports the len, and the before and after calls. All you do is build your actions class in PHP, and plop it in the proper directory. I used this (http://squio.nl/blog/2006/10/17/php-refection-and-json-stream-your-objects/) reference as a guide for working with Reflections in PHP.

The router.php is very similar to the example one provided with the release. I have added some comments, but the only portion that I really altered was the RPC portion. That has been completely redone.

baseAPI.php has been replaced by extBaseProvider.php and there is now a small additional utils.php file.

The attachment can be unzipped in the examples/direct/ directory (make a backup of yours first). Be sure to read the embedded comments for additional info. Maybe this will help someone. Just thought I would share.

ckr
31 May 2009, 7:10 PM
Here (http://www.arenak.com/ext/examples/direct/direct-demo.php) is a demo using the above. The chart update portion is taken from here (http://extjs.com/forum/showthread.php?p=324039#post324039), but re-written using Ext.Direct, and this PHP back end.

ckr
4 Jun 2009, 5:07 AM
Since I never heard anything on this (http://extjs.com/forum/showthread.php?p=334968#post334968) :-/ and now that we have some better examples with 3.0RC2, there is one change that is needed in the utils.php to support multiple server side action classes. Red code is removed, Blue is the replacement.

utils.ph - function buildAPI


function buildAPI($myObj)
{
$myClass = new ReflectionClass($myObj);
$myProp = array();
$data = array();

foreach(array_values($myClass->getProperties()) as $property)
{
if ($property->isPublic())
{
$propName = $property->name;
$propValue = $property->getValue($myObj);

if ((!is_object($propValue))&&(!is_array($propValue)))
{
$data[$propName] = $propValue;
}
else
{
// Added this bit, in the event that this
// should be an array (which I think it should be
// but for now will leave as is)
//
if (is_object($propValue))
{
$data[$propName] = getActions($propValue);
}
elseif (is_array($propValue))
{

foreach($propValue as $obj)
{
$data[$propName][] = getActions($obj);

if (!array_key_exists($propName, $data))
{
$data[$propName] = getActions($obj);
}
else
{
$data[$propName] = array_merge($data[$propName], getActions($obj));
}

}
}
else
{
die("Unexpected data type encountered...");
}
}
}
}
return $data;
}
This change basically flattens the array of the server classes, so that when the JSON is built, it adds the server classes as a property to the Ext.RemotingProvider actions object.

Once this change is in place, all of the new demos work.

ckr
5 Jun 2009, 5:23 AM
See first post for new attachment with latest fixes.

ckr
17 Jun 2009, 7:57 PM
Another small update to the router when passing more than one parameter.

Updated the demo page here (http://www.arenak.com/ext/examples/direct/direct-demo.php), all using this interface.

Updated the zip on the first post.

Stju
9 Jul 2009, 7:27 AM
Hi!
I am experiencing weird behaviour using this backend.
The problem persists if I want to have more than one parameter,for example if they are two, then first parametr received have both sent values comma separated, and second is undefined..
Any ideas?


<?
class TestClass{

public function doEcho($data, $second_parameter){
return 'DATA- '.$data.' second_parameter- '.$second_parameter;
}

}
?>from examples.. :

handler: function(){
TestClass.doEcho(text.getValue(), 'value2', function(result, e){
...


POST :
{"action":"TestClass","method":"doEcho","data":["hello","value2"],"type":"rpc","tid":2}

Response:
<b>Warning</b>: Missing argument 2 for TestClass::doEcho(), called in /router.php on line 190 and defined in <b>TestClass.php</b> on line <b>4</b><br />
<br />
<b>Notice</b>: Undefined variable: second_parameter in <b>TestClass.php</b> on line <b>5</b><br />
{"type":"rpc","tid":2,"action":"TestClass","method":"doEcho","result":"DATA- hello,value2 second_parameter- "}



Thanks in advance.

ckr
9 Jul 2009, 11:18 AM
Fixed!

See first post for download of new files (router.php was the problem).

meroy
22 Aug 2009, 4:25 PM
Thank you so much for this contribution. I wanted to learn from your example and saw that the TaskAction.php file inside php-lite.zip is missing these functions: updateChart, fileList, startBG and chkLoad. Anyway, I got this to work. This is what I created for startBG and chkLoad for the progress bar -- the 2 functions I was interested in.



public function startBG($dummy)
{
// generate a 20 digit task id
list($usec, $sec) = explode(' ', microtime());
$taskid = $sec . substr($usec, 2, 6) . rand(1000, 9999);

// change this to c:/<path>/... if running under windows
$statFile = '/tmp/stat-' . $taskid;
$dataFile = '/tmp/data-' . $taskid;

// set progress to 0 initially
file_put_contents($statFile, 0);

// build command string for background task
$cmd = '/usr/bin/php tasks/bg_task.php';
$cmd .= " --stat='{$statFile}' --data='{$dataFile}'";

// run the task in the background -- two examples are shown -- I used shell_exec here
## exec("{$cmd} >/dev/null 2>&1 &");
$pid = rtrim(shell_exec("nohup {$cmd} >/dev/null 2>&1 & echo $!"));

// return paths for the stat and data files
$send = "{$statFile},{$dataFile}";
return($send);
}

public function chkLoad($statFile, $dataFile)
{
while (!file_exists($statFile)) {
usleep(330000);
}

$progress = file_get_contents($statFile);
unlink($statFile);

if ($progress < 100) {
return('READ:'.$progress);
} else {
$answer = file_get_contents($dataFile);
unlink($dataFile);
return("Done: The answer to the question is {$answer}!");
}
}


The code for bg_task.php is as follow: installed as php/tasks/bg_task.php



<?php

unset($statFile);
unset($dataFile);

$c = 1;

while (isset($argv[$c])) {
if (preg_match('/^--stat=/', $argv[$c])) {
list($arg, $statFile) = explode('=', $argv[$c], 2);
}
else if (preg_match('/^--data=/', $argv[$c])) {
list($arg, $dataFile) = explode('=', $argv[$c], 2);
}
++$c;
}

if (!isset($statFile) || !isset($dataFile)) {
echo "usage: {$argv[0]} --stat=<statfile> --data=<datafile>\n";
exit(1);
}

// let's simulate a long running process
usleep( 750000); file_put_contents($statFile, 9);
usleep(1500000); file_put_contents($statFile, 29);
usleep(1500000); file_put_contents($statFile, 49);
usleep(1500000); file_put_contents($statFile, 69);
usleep(1500000); file_put_contents($statFile, 89);
usleep( 750000); file_put_contents($statFile, 99);

// write the answer to the data file
file_put_contents($dataFile, 42);

// let's simulate some clean up time and state 100% afterwards
usleep(1750000); file_put_contents($statFile, 100);

?>


Your demo helped me to understand Ext.Direct. Thank you.

ckr
24 Aug 2009, 4:51 AM
Hi Meroy!

Ya, I did not supply those routines mainly because you would have to make specific changes to accommodate whatever type of OS you were running on. Unfortunately my little sandbox is running Windows.

What you came up with is very close to what I threw together.

Remember this is just for testing and providing a demo. More care should be taken if implementing in a production environment. Here is my TestAction.php



////////////////////////////////////////////////////////////////////////////////
class TestAction
{
////////////////////////////////////////
//
// before and after functions are not exposed to Ext.Direct, however
// the router will pick up these two key named functions and
// call them as thier names suggest.
//
// before - is called before the requested method
// after - is called after the requested method
//
// Note: These are at the action class level.
// Meaning that if either before or after are defined they will
// be executed EVERYTIME ANY function within this action class is called
//
// By passing in the function that is to be called, you can put a test
// for certain methods, in the event that you may not want to do
// any pre or post processing for select methods (basically allowing
// you to do pre/post processing at the Action Class level and the
// the function called level.
//
////////////////////////////////////////

public function after($method, $data)
{
$myAfter = array();
// Add as needed

if ($method == "multiply")
{
// POST processing for the multiply function
// Would go in here.
$myAfter['type'] = "event";
$myAfter['name'] = "message";
$myAfter['data'] = "POST Processing - AFTER Multiply";
}

// POST processing for TestAction Class
// Would go here

return ($myAfter);
}

public function before($method, $data)
{
$myBefore = array();
// Add as needed

if ($method == "multiply")
{
// PRE processing for the multiply function
// Would go in here.
$myBefore['type'] = "event";
$myBefore['name'] = "message";
$myBefore['data'] = "PRE Processing - BEFORE Multiply";
}

// PRE processing for TestAction Class
// Would go here

return ($myBefore);
}

////////////////////////////////////////
//
// All the functions here can be
// called by Ext.Direct
//
////////////////////////////////////////

public function multiply($factor)
{
if (is_array($factor))
{
$num = $factor[0];
}
else
{
$num = $factor;
}
return ($num*9);
}
public function doEcho($data)
{
$pre = "ECHO:";
if (is_array($data))
{
$echo = $data[0];
}
else
{
$echo = $data;
}

return ($pre.$echo);
}
public function fileList($data)
{
if (is_array($data))
{
$path = $data[0];
}
else
{
$path = $data;
}
$list = scandir($path);
return ($list);
}
public function startBG($data)
{
// Fire off the background process
// and return the status handle for
// future checks
if (preg_match("/Win/", php_uname('s')))
{
// All things required in Windows to start
// a background process
$UNIQUE = str_replace('.','',microtime(true));
$TITLE = "\"myProcess\" ";
$SHELL = "start ";
$NOWIN = "/B ";

// PHP executable to run a
// PHP script from the command shell
$PHP = "c:/php/php.exe";

// Back Ground Process to kick off
$BGP = "c:/bg-processes/bgProcess.php";

// Files that will act as named pipes
//
$STAT = "c:/pipes/stat-".$UNIQUE;
$DATA = "c:/pipes/data-".$UNIQUE;

$ARGS = $BGP." ".$STAT." ".$DATA;

pclose(popen($SHELL.$TITLE.$NOWIN."\"".$PHP."\" ".$ARGS, "r"));
return ("$STAT,$DATA");
}
else
{
throw new Exception('Unexpected OS found! From loadServers method.');
}
}
public function chkLoad ($myStatFile, $myDataFile)
{
$statFile = str_replace("\"", "",$myStatFile);
$dataFile = str_replace("\"", "",$myDataFile);


// Allow the server process to wait for a status.
// this eliminates the client from continually polling
// the server, more polls by the client more resources
// on the server used.
//
while (!file_exists($statFile))
{
sleep(2);
}

$RP = fopen($statFile, "r");
$data = trim(fgets($RP));
fclose($RP);
unlink($statFile);

if ($data == "Process Completed")
{
$DP = fopen($dataFile, "r");
$result = trim(fgets($DP));
fclose($DP);
sleep(1);
unlink($dataFile);
$ret = "Done:".$result;
}
else
{
$ret = "READ:".$data;
}

return ($ret);
}

public function updateChart($data)
{
srand();
$retValue = rand(150000, 650000);
$sleepTime = rand(2, 10);
sleep($sleepTime);

$polled=date('g:i:s a');

$send = array('time'=>$polled, 'widgets'=>$retValue);

return($send);
}

function getTree($id){
$out = array();
if($id == "root"){
for($i = 1; $i <= 5; ++$i){
array_push($out, array(
'id'=>'n' . $i,
'text'=>'Node ' . $i,
'leaf'=>false
));
}
}else if(strlen($id) == 2){
$num = substr($id, 1);
for($i = 1; $i <= 5; ++$i){
array_push($out, array(
'id'=>$id . $i,
'text'=>'Node ' . $num . '.' . $i,
'leaf'=>true
));
}
}
return $out;
}

}


And here is the bgProcess.php



$statFile = $argv[1];
$dataFile = $argv[2];

for($i=0; $i<100; $i++)
{
$i=$i+9;
$SF = fopen($statFile, "w");
fwrite($SF, "$i\n");
fclose($SF);
sleep(1);
}

$SF = fopen($statFile, "w");
fwrite($SF, "Process Completed\n");
fclose($SF);

$DF = fopen($dataFile, "w");
fwrite($DF, "The answer to the question is 42!\n");
fclose($DF);
sleep(5);

exit;


Hope that helps!

meroy
24 Aug 2009, 6:47 AM
Thank you ckr.

meroy
24 Aug 2009, 12:54 PM
I now see the need to sleep at the server side. I updated my post #9 -- added the following to the chkLoad function.


while (!file_exists($statFile)) {
usleep(330000);
}

steffenk
26 Aug 2009, 12:59 AM
Hi ckr,

thanks for your example, it helped me a lot even as i had to modify for my needs.

Why did you ripped the cache for the API? Saves a lot of time.

ckr
26 Aug 2009, 6:00 PM
Thanks Steffenk.

Ripped the cache for the API? I do not understand the question. Sorry.

steffenk
27 Aug 2009, 12:53 AM
i mean this found in Tommy's php-source:
class ExtDirect_CacheProvider

and

$api->setCacheProvider($cache);

Reason: the api generation can be time expensive because of reflection class. The CacheProvider caches the output of api.php

ckr
27 Aug 2009, 2:27 AM
Oh, Tommy's and mine are completely different and independent. I had this one up (to get something out there), while Tommy was still working on his.

I have said (in my 2nd post) if you want a more robust PHP solution you should look at Tommy's. I considered mine more of a "Lite" implementation that you should be able to pickup and run with quickly. ;)

steffenk
27 Aug 2009, 2:31 AM
ok, nevermind, i'm able to combine it. Most important is to understand the way how it works.

ckr
27 Aug 2009, 4:21 AM
If I get a chance, I will look at it, and see if I can add it in. Work keeps getting in the way, so no promises. :D

Jubei
27 Oct 2009, 6:01 PM
This works very well! Thank you sir! :D

Cambiata
25 Jan 2010, 9:36 AM
Hi! Great lib!

It seems like the php-lib download is out of sync with your direct-demo though.

1. JavaScript error: out.el not defined (Might be caused by the current 3.1?)
2. There seems to be missing actions for fileList, updateChart etc...

Corrections or new exciting versions in the pipe? :-)

Regards / Jonas

ckr
25 Jan 2010, 10:46 AM
Hey Cambiata. Thanks for the heads up.

Real work has been keeping me busy. I will look into it and let you know.

ckr
26 Jan 2010, 4:42 AM
Ok - So the out.el not defined was simply an error when attempting to write out to the status window before it was actually rendered. So now when you load the demo page, you will see the status window (some might not have even noticed that it was there), and then it will collapse. You can expand it at any time. I have dumped this demo in my 3.1 install and everything is work fine.

I did make a few small changes, see the first post in this thread.

Enjoy!

Dumbledore
18 Feb 2010, 6:29 AM
Hi,

i can´t figure out how to handle PHP-Sessions with your Ext.Direct Implementation.
Can you offer an example?

Bye, Dumbledore

Dumbledore
18 Feb 2010, 10:05 AM
Nice Ext-Direct Class. Found a way to include correct PHP Sessions.
In my case it needs only a session_start() in config.php

Bye, Dumbledore

ckr
18 Feb 2010, 11:51 AM
Yes, for just a simple session start, that is where I would put it.

The sessions are so application specific that I never added anything for session support. But I guess I should put some info/documentation in there so that others can find what you have already discovered.

Good Luck!

cybervirax
22 Mar 2010, 8:55 AM
Thx for direct php.

It's possible to see the source of your demo page ?

There are some interesting php source that I want to test.

Thx

Ro'

ps: sorry if i don't speak english very well...i'm french ;)

ckr
23 Mar 2010, 5:14 AM
Sure, I will zip it up. Nothing special really.

cybervirax
23 Mar 2010, 11:30 AM
Sure, I will zip it up. Nothing special really.

thx, i will see ;)

ckr
25 Mar 2010, 2:53 AM
Here is a snap shot of the demo. Enjoy.

meroy
25 Mar 2010, 11:01 PM
Thank you very much. I'm quite sure that this will help a lot of folks out there.

crasy
23 Oct 2011, 3:51 PM
Here is a snap shot of the demo. Enjoy.

Hi ckr,

does this sample work with v4 beyond ?
i didnt work with v3 so i have no experience with extjs.
i was looking after a solution to fix columns in a table and was stumple over sencha, thats why i´m a blody noob.
up to now i wrote all my code by my self and just use prototype/scriptaculous, and i have mutch to learn.
so please excause me if i ask a dump question, thanks a lot :)

crasy

Edit:
i tryed to change the pathes in the "direct-demo.php", but i get this errors:


invalid XML attribute value
<frame name="head" src="head.php" scrolling=no> (http://www.sencha.com/forum/&quot;{href}&quot;)=> ext-all.js (Zeile 15)

Ext.tree.TreeLoader is not a constructor
directFn: TestAction.getTree => direct-demo.js (Zeile 161)

Dumbledore
25 Oct 2011, 10:32 AM
Hmm... There is no Ext.tree.Treeloader in Ext4... In Ext4 you must use TreeStore...

Bye, Dumbledore

ckr
1 Nov 2011, 3:32 AM
Crasy - Sorry, I have not kept up with this for awhile now (new job, online server crashed, and life). I have not looked at 4 yet, but I know there are significant changes that would have to take place.

I might have some time next week to look at and get back into this.

ckr
5 Jan 2012, 3:05 PM
I have resurrected this from the grave and instead of running this from my home, I have a real web hosting service. ;)

I have enabled the original demo using Extjs 3.2 (the latest that I had worked with - I know it has been awhile) but there is one problem. The original was running on windows. This is now hosted on a Linux server, which is good but I have to recode some of the server backend. So for now, everything in the demo is working except the background process. I will fix it soon. After which I plan on porting this all over to Extjs 4 and start playing around with Touch.

I updated the link to the demo, but you can hit it from here (http://www.arenak.com/ext/examples/direct/direct-demo.php) too.

bartonjd
11 Jun 2012, 1:30 PM
How well is this running in ExtJs 4?