PDA

View Full Version : Extremely Easy Ext.Direct integration with PHP



j.bruni
23 Jun 2010, 5:38 AM
*** Compatible with ExtJS 4 ***

This is an updated / refactored version of "Easy Ext.Direct integration with PHP (http://www.sencha.com/forum/showthread.php?95022-Easy-Ext.Direct-integration-with-PHP)"

How to use:

1) PHP


<?php

require 'ExtDirect.php';

class Server
{
public function date( $format )
{
return date( $format );
}
}

ExtDirect::provide( 'Server' );

?>Here, "Server" is the PHP class that we want to provide access from the JavaScript code. It could be any other class.

2) HTML:


<script type="text/javascript" src="ext-direct.php?javascript"></script>Here, "ext-direct.php" points to the PHP file shown on item 1. The "?javascript" query string is necessary, because the default output is on JSON format (good for Ext Designer).

3) JavaScript:


Ext.php.Server.date( 'Y-m-d', function(result){ alert( 'Server date is ' + result ); } );Here, to call the "date" method from PHP "Server" class, we prepended the default namespace Ext.php. The first parameter is the $format parameter. The second parameter is the JavaScript callback function that will be executed after the AJAX call has been completed. Here, an alert box shows the result.

=D>

What are you waiting for the download?

It includes:

ExtDirect.php - This is the file you include in your PHP script.
example.php - This is a working sample (PHP part).
example.html - The HTML and JavaScript parts of the working sample.

In the next post, I tell you about the features and configuration options.

Thanks.


The current latest version is ExtDirect_2011-11-08.zip, which you can download below:

khebs@live.com
23 Jun 2010, 9:00 AM
You are brilliant!

j.bruni
23 Jun 2010, 10:33 AM
Thanks, Khebs!

You haven't seen nothing yet! ;)

Look at the Features

- API declaration with several classes (not limited to a single class)

- API "url", "namespace" and "descriptor" settings ("ExtDirect" class assigns them automatically if you don't)

- Two types of API output format: "json" (for use with Ext Designer) and "javascript" (default: json)

- You choose if the "len" attribute of the actions will count only the required parameters of the PHP method, or all of them (default: all)

- You choose whether inherited methods will be declared in the API or not (default: no)

- You choose whether static methods will be declared in the API or not (default: no)

- Instantiate an object if the called method is static? You choose! (default: no)

- Call the class constructor with the actions parameters? You choose! (default: no)

- "debug" option to enable server exceptions to be sent in the output of API action results (default: off)

- "utf8_encode" option to automatically apply UTF8 encoding in API action results (default: off)

Learn how to use all these configuration options in the next post. It is so simple!

j.bruni
23 Jun 2010, 11:03 AM
Hey! Forgot to mention in the features list:

- Handles forms

- Handles file uploads

j.bruni
23 Jun 2010, 11:13 AM
Easy.

If the configuration option name is "configuration_name" and the configuration value is $value, use this syntax:


ExtDirect::$configuration_name = $value;And that's all.

Now, let's see the available configuration options:

- name: api_classes
- type: array of strings
- meaning: Name of the classes to be published to the Ext.Direct API
- default: empty
- comments: This option is overriden if you provide a non-empty $api_classes parameter for the "ExtDirect::provide" method. Choose one or another. If you want to declare a single class, you can set $api_classes as a string, instead of an array.
- example
ExtDirect::$api_classes = array( 'Server', 'OtherClass', 'StoreProvider' );- name: url
- type: string
- meaning: Ext.Direct API attribute "url"
- default: $_SERVER['PHP_SELF']
- comments: Sometimes, PHP_SELF is not what we want. So, it is possible to specify the API URL manually.
- example
ExtDirect::$url = '/path/to/my_php_script.php';- name: namespace
- type: string
- meaning: Ext.Direct API attribute "namespace"
- default: "Ext.php"
- comments: Feel free to choose your own namespace, according to ExtJS rules for it.
- example
ExtDirect::$namespace = 'Ext.Dharma';- name: descriptor
- type: string
- meaning: Ext.Direct API attribute "descriptor"
- default: "Ext.php.REMOTING_API"
- comments: Feel free to choose your own descriptor, according to ExtJS rules for it, and to the chosen namespace.
- example
ExtDirect::$descriptor = 'Ext.Dharma.REMOTING_API';- name: count_only_required_params
- type: boolean
- meaning: Set this to true to count only the required parameters of a method for the API "len" attribute
- default: false
- example
ExtDirect::$count_only_required_params = true;- name: include_static_methods
- type: boolean
- meaning: Set this to true to include static methods in the API declaration
- default: false
- example
ExtDirect::$include_static_methods = true;- name: include_inherited_methods
- type: boolean
- meaning: Set this to true to include inherited methods in the API declaration
- default: false
- example
ExtDirect::$include_inherited_methods = true;- name: instantiate_static
- type: boolean
- meaning: Set this to true to create an object instance of a class even if the method being called is static
- default: false
- example
ExtDirect::$instantiate_static = true;- name: constructor_send_params
- type: boolean
- meaning: Set this to true to call the action class constructor sending the action parameters to it
- default: false
- example
ExtDirect::$constructor_send_params = true;- name: debug
- type: boolean
- meaning: Set this to true to allow exception detailed information in the output
- default: false
- example
ExtDirect::$debug = true;- name: utf8_encode
- type: boolean
- meaning: Set this to true to pass all action method call results through utf8_encode function
- default: false
- example
ExtDirect::$utf8_encode = true;- name: default_api_output
- type: string
- meaning: API output format - available options are "json" (good for Ext Designer) and "javascript"
- default: "json"
- comments: Another way to enforce "javascript" output is to append the "?javascript" query string in the end of your PHP script URL; do this in the HTML <script> tag that refers to your API
example
ExtDirect::$default_api_output = "javascript";

khebs@live.com
23 Jun 2010, 12:07 PM
Yes i was playing with it, very good indeed.. and nice one w/ json & javascript ;) nothing is much simpler than this.. thanks for sharing! :)

Andrew Peacock
23 Jun 2010, 12:44 PM
Thanks J. Saw your response in your other post, skipped over here, and like what I see. Thanks for sharing with everyone. Andy

ezkorpyo
24 Jun 2010, 8:50 AM
Hello, how run the example ? ? ?

Thanks. . .

j.bruni
25 Jun 2010, 3:40 PM
Hello, how run the example ? ? ?

Thanks. . .

1. Download the "ExtDirect.php.zip" file from post #1 of this thread.

2. Unzip the file

3. Copy the extracted files (ExtDirect.php, example.php, and example.html) to your web site folder (requires PHP and and HTTP, like Apache or NginX)

4. Using your browser, go to the URL where you can access "example.html"... it can be "http://localhost/example.com" or something else, according to your settings.

Hope this helps...

khebs@live.com
27 Jun 2010, 9:46 AM
Hi i think i found a bug on your ExtDirect.php class.. when i use multiple classes i got an error at line: 381, stating that it is passing a string not an array.. so a quick fix is..

@ExtDirect.php, line 381




if (!is_array($this->parameters))
$this->parameters = array($this->parameters);

return call_user_func_array( array( $this->instance, $this->method ), $this->parameters );



Classes:

>> Cds.php



<?php

class Cds
{
public function getAll()
{
$db = Crystal::db();
return $db->get('cds')->fetch_all();
}
}
?>


>> Server.php



<?php

class Server
{
public function date($timezone = 'Asia/Urumqi')
{
date_default_timezone_set( $timezone );
return date( 'm/d/Y h:i:s a' );
}
}

?>


>> api.php



<?php
## @required php's
require 'libraries/ExtDirect.php';
require 'libraries/crystal-0.4/Crystal.php';
require 'classes/Server.php';
require 'classes/Cds.php';

ExtDirect::provide(array('Server','Cds'));
?>

grgur
27 Jun 2010, 10:58 AM
Sweet, excellent job, J!
I had the same issue as khebs, thanks for the fix.

Well, how about the formHandler support?

My quick fix would be

ExtDirect.php line 114

// Count only required parameters or count them all, according to "count_only_required_params" configuration
if ( self::$count_only_required_params )
$newMethod = array( 'name' => $method->getName(), 'len' => $method->getNumberOfRequiredParameters() );
else
$newMethod = array( 'name' => $method->getName(), 'len' => $method->getNumberOfParameters() );

if (in_array(Array("$class"=>$method->getName()),self::$formHandlers)) $newMethod["formHandler"]=true;
$methods[]=$newMethod;

and in your api.php (or example.php) I do this:

ExtDirect::$formHandlers = Array(Array('someClass'=>'someFunc'));

at this point we need to adapt parameters sent by the forms to params received by the func. The quick and dirty fix is

...
function someFunc($data) {
global $_REQUEST;
$data = $_REQUEST;
...
}


This is a 1 min fix. I would love to see official support by the developer

khebs@live.com
27 Jun 2010, 2:46 PM
+1, I would like to see official fix from the developer as-well..

j.bruni
29 Jun 2010, 10:32 AM
Hi i think i found a bug on your ExtDirect.php class.. when i use multiple classes i got an error at line: 381, stating that it is passing a string not an array..

Thanks for the bug report.

It is not related to the number of classes, though. It is related to the number of parameters of the method.

If the method does not require any parameter (zero required parameters), i.e., when "len" = 0 (zero) in the API declaration, Ext sends "null" as data (instead of an empty array)... this "null" is interpreted as an empty string when PHP reads the JSON input...

Anyway, I have just coded the fix and I will soon upload the modified code.

I appended the following two lines to ExtDirectAction::__construct



if ( empty( $this->parameters ) )
$this->parameters = array();

j.bruni
29 Jun 2010, 11:49 AM
Sweet, excellent job, J!

Well, how about the formHandler support?

I would love to see official support by the developer

Thanks, grgur!

I will add formHandler support.

Soon I will upload the new version with the new feature.

j.bruni
3 Jul 2010, 10:24 PM
A new version has been released today.

The biggest change is the formHandler support.

Another important change was the bug fix for methods with zero parameters (len = 0).

Thanks for grgur and khebs for the valuable feedback.

Below, the complete changelog. In the next post, I will share the exciting details about the new formHandler support!

Thanks.


COMPLETE CHANGELOG:

- Added "form_handlers" configuration option to flag method(s) as formHandler
- Added "@formhandler" doc comment check to automatically flag a method as formHandler
- Added ExtDirectAction::$form_handler boolean property
- Added "form_handler" parameter to ExtDirectAction constructor, to initialize "form_handler" property value
- Implemented special "parameters" array preparation for formHandler methods

- Included comment to ExtDirect::provide method

- FIX: "rpc" extType is checked on form actions
- FIX: "include_inherited_methods" configuration is checked on action call
- BUG FIX: empty "parameters" is now transformed into an empty array (when "len" = 0, ExtJS sends data = "null")
- BUG FIX: "upload" boolean is now set correctly
- DOC FIX: ExtDirectAction::$parameters is of type "array" (not "mixed")

j.bruni
3 Jul 2010, 11:18 PM
There are two different ways to flag a method as a "formHandler".


Method 1: use the new ExtDirect::$form_handlers configuration option.

- name: form_handlers
- type: array of strings
- meaning: Name of the class/methods to be flagged as formHandler in the Ext.Direct API
- default: empty
- comments: The string format for each method must be "className::methodName".
- example


ExtDirect::$form_handlers = array( 'someClass::someMethod', 'Server::date' );Method 2: include "@formHandler" in the DOC comment of the method.

Example:


class FTP_Manager
{
/**
* Sets FTP password for a specific account
*
* @formHandler
* @param string $account Name of the account
* @param string $password New password
* @param string $password_confirm New password confirmation
* @return string
*/
public function set_ftp_password( $account, $password, $password_confirm )
{
// do stuff
return $result;
}
}In the example above, due to the "@formHandler" string inside the method's DOC comment, it will be flagged as a "formHandler" method.

It has the same effect as this:


ExtDirect::$form_handlers[] = 'FTP_Manager::set_ftp_password';* Receiving parameters *

The parameters sent by forms are adapted to be received by the class method.

Pay attention now, because this is not usual.

I will use the "set_ftp_password" method above as the example.

First, note that we don't want that all formHandler methods have the same not-friendly signature, like this:


function set_ftp_password( $data );
function do_something( $data );
function do_something_completely_different( $data );
Where $data is the user input (usually $_POST)

So, to be able to keep normal method signatures, like this...


function set_ftp_password( $account, $password, $password_confirm )...I have implemented the following solution:

When the method/action function is a formHandler, its parameter values are taken from the input names that matches the parameter's names.

So... $_POST['account'] will automatically become the $account parameter...

$_POST['password'] value will be the $password parameter value...

...and from where the $password_confirm parameter value will come from? Yes! From $_POST['password_confirm']

That's it: the method's parameters' names matches the $_POST array keys.

Advantages:

- Don't need to worry with parameter order
- Can use meaningful and clean method/function signature
- Don't need to sniff with $_POST array - the ExtDirect controller does this for us (forget "isset" checkings... if a certain parameter value is not set in the $_POST array, the default value - if available - or null is passed to the method/function)

Disadvantages:

- The input names must match the method/function parameter names (IMHO, an advantage!)
- This approach may just not be the best for you (in this case, just ignore all this stuff and go for the $_POST / $_GET / $_REQUEST arrays!)

Of course, all validation / filtering / sanitization of input data, as always, must be carefully considered.


Additional note about file upload:

If your file input name is "userfile", it will be available in your method/function parameter named $userfile

In other words: your $userfile parameter will receive the value from $_FILES['userfile']

netsuo
7 Jul 2010, 5:51 AM
A lot of thanks for your great integration !

One thing though: be aware that the functions you call need "return $blahblah" and not "echo $blahblah".
For wathever reasons I was echo'ing my return values in my class and when you do this, PHP's function "call_user_func_array" used in ExtDirect.php not only returns nothing, but nothing is executed in the PHP code after that line. So ExtDirect.php didn't return the complete response and ExtJS was crashing clientside.

j.bruni
7 Jul 2010, 10:46 AM
A lot of thanks for your great integration !

One thing though: be aware that the functions you call need "return $blahblah" and not "echo $blahblah".
For wathever reasons I was echo'ing my return values in my class and when you do this, PHP's function "call_user_func_array" used in ExtDirect.php not only returns nothing, but nothing is executed in the PHP code after that line. So ExtDirect.php didn't return the complete response and ExtJS was crashing clientside.

Thanks, netsuo.

You are pointing to two different issues.

*** Issue #1 = echo vs. return

Yes, the API functions (methods) should return the values, not echo them. If you echo or print anything, these outputs will corrupt the final JSON which the ExtDirect classes prepare.

It is always a good idea to think in a "MVC" (model view controller) way - or at least in a VC (view / controller) way. I like to always do all calculation and collect all data first, and only in a final and separated step take care of the "view" (truly, the "output").

*** Issue #2 = "nothing is executed in the PHP code after that line" = exit

If you look at line 480 of ExtDirect.php file, you will see an exit(); command, in the constructor method of the ExtDirectController class.

This is why nothing is executed after a call to ExtDirect::provide()

In fact, ExtDirect::provide is merely an alias to new ExtDirectController(), i.e., ExtDirectController::__construct

This constructor accepts two parameters. The first is the $api_classes array, where you define the classes you want in your API. The second is an optional parameter: $autorun. It defaults to true.

Just look at ExtDirectController::__construct full code:



public function __construct( $api_classes = null, $autorun = true )
{
if ( is_array( $api_classes ) )
ExtDirect::$api_classes = $api_classes;

elseif ( is_string( $api_classes ) )
ExtDirect::$api_classes = array( $api_classes );

$this->request = new ExtDirectRequest();
$this->response = new ExtDirectResponse();

if ( $autorun )
{
$this->run();
$this->output();
exit();
}
}The $autorun parameter makes the controller call its "run" and "output" methods, and then exit.

Instead of calling ExtDirect::provide( $api_classes ), you may want to make a call setting $autorun to false:


$extdirect = new ExtDirectController( $api_classes, false );If you do this, you will need to manually call:


$extdirect->run();This will run the controller (i.e., process the HTTP request, and generate the corresponding HTTP response).

So, after this, all response (output) will be available on $extdirect->response

This output is only echoed when you call the output method, if you want:


$extdirect->output();Makes sense?

netsuo
8 Jul 2010, 3:43 AM
I'm sorry I think you misunderstood what I tried to say (English is not my language).

I was just saying that if you echo the value in the class you're linking with extdirect, the JSON won't be correct AND the PHP function "call_user_func_array" that you use in your ExtDirect.php will crash without any errors nor log.
It simply crashes and all code that's after "call_user_func_array" will not be executed (you can test yourself by echo'ing value in the function that "call_user_func_array" call and adding somethin' like "echo 'test';" after that line in ExtDirect.php).

It looks like PHP doesn't like at all that you're echo'ing something in a function called by "call_user_func_array".

j.bruni
8 Jul 2010, 6:25 AM
I'm sorry I think you misunderstood what I tried to say (English is not my language).

I was just saying that if you echo the value in the class you're linking with extdirect, the JSON won't be correct AND the PHP function "call_user_func_array" that you use in your ExtDirect.php will crash without any errors nor log.
It simply crashes and all code that's after "call_user_func_array" will not be executed (you can test yourself by echo'ing value in the function that "call_user_func_array" call and adding somethin' like "echo 'test';" after that line in ExtDirect.php).

It looks like PHP doesn't like at all that you're echo'ing something in a function called by "call_user_func_array".

Hi, netsuo. I understood you well. Your English is good.

I haven't done any test to check what you said, when I wrote my previous reply to you.

Right now I did. I changed an API function so it calls echo instead of returning a value. I also added more code after call_user_func_array to see what happened.

For me it does not crash at all.

The API function:


class SW_LayoutManager
{
function get_layouts_data()
{
echo 'tralala';
}
}
And after the "call_user_func_array" in ExtDirect.php I added:


echo 'trilili';
Below, the response:


tralalatrilili{"type":"rpc","tid":6,"action":"SW_LayoutManager","method":"get_layouts_data","result":null}

FYI, I am using Firebug Console to see the server response.

So, according to my experience, echo in an API function/method does not crash, and the code after call_user_func_array is normally executed.

As I said, we have two different issues:

1) "the JSON won't be correct"

This is true. The echo corrupts the JSON, as we can see in the example above.

2) "the PHP function 'call_user_func_array' that you use in your ExtDirect.php will crash without any errors nor log"

This is not true. I think it depends on PHP version and/or settings, operational system, etc.

I am using Linux Ubuntu 10.04 (Lucid Lynx) and PHP 5.3, with default php.ini settings.

I can call echo, and "call_user_func_array" does not crash for me. And I believe it really shouldn't crash.

It would be nice if we had more feedback from another users...

What is your OS and PHP version?
Are you sure "echo" is the problem?
Can you post some of this PHP code?

Anyway, thanks a lot for your feedback.

netsuo
9 Jul 2010, 12:48 AM
Hi !

I think I've found what caused my problem and you where true, the echo wasn't the problem.
I don't know why (maybe a copy/paste error between two projects I'm working on), but after my "echo" there was a "die();"... now it explains all ;-)

Sorry to have taken your time away. Rest assured I will report anything (real) I'll found about your ExtDirect, because I'll use it extensively the next weeks in a project I'm starting right now.

Thanks a lot to have taken some time to help me, it definately gave me some advice on my problem !

j.bruni
9 Jul 2010, 9:41 AM
Sorry to have taken your time away. Rest assured I will report anything (real) I'll found about your ExtDirect, because I'll use it extensively the next weeks in a project I'm starting right now.

Thanks a lot to have taken some time to help me, it definately gave me some advice on my problem !

No problem. In fact, I thank the opportunity to share here in the forums a bit more information about "ExtDirect.php" internals. Also, each post makes the thread stay on top of the latest updated! :)

I thought the cause of your problem could be also something related to the "header" command in ExtDirectController::output... but now you already found the real cause.

I am using this "ExtDirect.php" in a project I am developing right now. More features were recently added! So, soon I will upload a new version.

Basically, three new configuration options were added, all of callback type: "authorization_function", "transform_result_function", and "transform_response_function"

You will be able to specify an "authorization_function", where you can check the user permissions and return true or false accordingly, allowing the API call or not.

With the "transform_result_function", you can modify the result of the API method call after its execution, but before it is sent to the client-side.

Finally, with the "transform_response_function", you can modify the response structure. This allows to fire server-side events! But I am not using this feature for it. I am using it to send extra server side data together with the RPC result.

So... stay tuned!

CYO

Andrew Peacock
12 Jul 2010, 7:02 AM
Great job J! I just implemented this as a test last night, and like it's simplicity.

One question though (which may or may not be specific to your code): I'm planning on using ExtJS's polling functionality soon - is there any way to pass a direct function into a polling provider (a bit like directFn for data stores)?

Regards,
Andy

aw1zard2
12 Jul 2010, 1:43 PM
Nice job on this! My company has a good jump ahead on this from compared to what you have made but you are getting the clear picture. ;)

Unfortunately I can't discuss in public what our company is doing, but if you need/want to chat about anything just shoot me a message.

Regards,
Ron

Andrew Peacock
13 Jul 2010, 8:12 AM
Hi all,
A word of advice:

This works:



ExtDirect::$form_handlers = array( 'Config::setConfig');
ExtDirect::provide( 'Config' );


This doesn't :-)


ExtDirect::provide( 'Config' );
ExtDirect::$form_handlers = array( 'Config::setConfig');


The latter method generates stack overflow errors when using multiple parameters, although it works fine with 1 parameter or 0 parameters, oddly.

Great work - I love the form handler shortcut of having parameters automatically defined via the $_POST parameter names. Very slick!

Andy

Andrew Peacock
14 Jul 2010, 5:58 AM
PROBLEM SOLVED: SEE ANSWER LATER IN THIS POST

Hi,
One question about form data: I'm using a checkbox which submits the value "on" if it is selected. However, if it's not selected, nothing gets submitted, and the direct function doesn't seem to run at all. Here's the function being called:



public function setProxiesConfig($proxies_enabled="") {
firebird("Proxies enabled = " . $proxies_enabled);
$this->setOption("proxies_enabled", $proxies_enabled);
$this->saveConfig();

$obj = array(
'success' => true
);

return $obj;
}
Firebird() is a self-written routine to dump the output to firePHP and a debug file as well. As it's the first line in the function, I would expect it to run, but it doesn't, which implies the function is not running.

The JSON response was:

{"type":"rpc","tid":"12","action":"ConfigManager","method":"setCaptchaConfig","result":"Exception"}

But there was no PHP error generated that I can see anywhere (and I've got PHP writting errors to a single log file regardless of which PHP file was running).

Can anyone offer any inspiration to get round this?

Regards,
Andy


SOLUTION FOUND:
Enable the option to only count mandatory parameters:

ExtDirect::$count_only_required_params = true;

Andrew Peacock
16 Jul 2010, 3:37 AM
Hi,
Anyone got any thoughts on how to do this within a polled operation?
Andy

Andrew Peacock
19 Jul 2010, 7:49 AM
OK, it's been a long day, I'm just back off my hols, and I'm new to this kind of complex JS, so I can't work this one out.

I've got the extdirect code working absolutely fine. But I'm having problems accessing the returned data FROM OUTSIDE THE EXTDIRECT CALL...

Here's the relevant code:



var plugins = ['plugin1', 'plugin2'];
var input="Anyone there? " ;
var hook='test';

function executeHook(hook, input) {
var output = "";
Ext.php.PluginManager.executeHooks(plugins, hook, input, function(data) {
console.info("DEBUG WITHIN FUNCTION: " + data);
output = data;
});
return output;
}

console.info("RESULT FROM FUNCTION:" + executeHook(hook, input));



Each plugin simply adds a piece of test to the input, in a chain. So the output should be:

"Anyone there? Hello from core; Hello from plugin 2; Hello from plugin 1; "



However, the console has the two output lines as follows:
"RESULT FROM FUNCTION:"
"DEBUG WITHIN FUNCTION: Anyone there? Hello from core; Hello from plugin 2; Hello from plugin 1; "

So the variable data is correct within the ExtDirect call, but how do I get access to the data outside the ExtDirect call?

I've also tried:



Ext.php.PluginManager.executeHooks(plugins, hook, input, function(data) {
console.info("DEBUG WITHIN FUNCTION: " + data);
return data;
});


But that produces the same result.

Any ideas?

Regards,
Andy

Trii
19 Jul 2010, 1:00 PM
@Andy

You must remember that the Ext.Direct calls are asynchronous which means you cannot count on the callback parameter being executed in a linear fashion like the rest of your function code. In your example the function executeHook() calls the Direct method which sets a callback, then it returns output, which is empty until the callback is called. Once the HTTP request initiated by the executeHooks() completes, it then calls your callback and sets the value of output but by then your initial function has returned. You will have to redesign your logic to handle this

Andrew Peacock
19 Jul 2010, 3:48 PM
Thanks Trii... I came to the same conclusion myself shortly afterwards. I typed that discovery into a post here, but I guess I forgot to press "submit". But thanks anyway - I appreciate your help.

Whilst you and I are here.... I don't suppose you've got any thoughts about how to use this integration in a polling process? I don't have a scooby where to start.

(Scooby => Scooby Doo => clue)

Regards,
Andy

Trii
19 Jul 2010, 6:49 PM
Thanks Trii... I came to the same conclusion myself shortly afterwards. I typed that discovery into a post here, but I guess I forgot to press "submit". But thanks anyway - I appreciate your help.

Whilst you and I are here.... I don't suppose you've got any thoughts about how to use this integration in a polling process? I don't have a scooby where to start.

(Scooby => Scooby Doo => clue)

Regards,
Andy

Look into Ext.util.TaskRunner() (http://www.sencha.com/deploy/dev/docs/?class=Ext.util.TaskRunner) You can use that to start a task that calls an arbitrary callback every interval. It's hard to give you a more detailed response without knowing more about what you are trying to poll and when you should start and when you should end. you can use your callback on the executeHooks to register a polling task with the task runner

Andrew Peacock
20 Jul 2010, 3:31 PM
Ah! Thanks Trii. I'd kind of glossed over that, misinterpreting what it was. But yes, that looks like it'll do the trick!

Thanks again for all your help,
Andy

netsuo
2 Aug 2010, 3:19 AM
Hi all,

I'm using this ExtDirect integration a lot and I found something that I want to change. If you setup a DirectStore with the "api" parameters (code below), "baseParams" are received server-side in one object, not multiples parameters.

DirectStore (incomplete code):


this.store = new Ext.data.DirectStore(
{
pruneModifiedRecord: true,
autoLoad: true,
api:
{
read : Ext.php.get.Store,
create : Ext.php.set.StoreCreate,
update : Ext.php.set.StoreUpdate,
destroy : Ext.php.del.Store
},
baseParams:
{
databaseTable: 'jos_gtr_batiment_bat',
displayField: 'nom_BAT',
valueField: 'id_BAT',
idField: 'id_BAT',
returnAllFields: true
},


Doing code above, I have to setup this PHP class:


class del
{

function Store($datas)
{
$database_table = $datas->databaseTable;
$idField = $datas->idField;


But I want to achieve this:


class del
{

function Store($databaseTable,$displayField,$valueField,$idField, $returnAllFields)
{


Any advice on how to do that ?

Thanks,

Stephane

azuroff
5 Aug 2010, 7:22 PM
Since I didn't see anything in the .zip file itself, what license is this code released under?

j.bruni
12 Aug 2010, 7:10 AM
Since I didn't see anything in the .zip file itself, what license is this code released under?

Sorry. In my ignorance, I see the whole licensing issue as complicated, and I never spent the time reading and studying it enough to have a good comprehension of the characteristics and differences between the most known open source licenses...

All I can tell is that what I currently think about the code license is something like:

1. You are free to do anything you want with it.
2. You are responsible for the consequences of the code usage (meaning: the author is not responsible if you misuse, or use it in an unsafe manner, etc.)
3. I will be happy if you don't remove the "author" comment in the code.

Hmmm... maybe I should add these words as a "license.txt" in the zip?

j.bruni
12 Aug 2010, 9:43 AM
A new version has been released today.

Just go to post #1 of this thread, and the new ZIP file is there.

Five new configuration options were added, four of them of callback (http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback) type: "declare_method_function", "authorization_function", "transform_result_function", and "transform_response_function"

Now, you are able to specify an "authorization_function", where you can check the user permissions and return true or false accordingly, allowing the API call or not.

With the "transform_result_function", you can modify the result of the API method call after its execution, but before it is sent to the client-side.

Finally, with the "transform_response_function", you can modify the response structure. This allows to fire server-side events, and to send extra server side data together with the RPC result.

I am too busy to write detailed instructions about the new features.

Here is some sample code:



// New configuration option
ExtDirect::$id = 'my_api';

// All "function" configurations accept parameters of callback type
ExtDirect::$transform_result_function = 'transform_result';
ExtDirect::$transform_response_function = 'transform_response';
ExtDirect::$declare_method_function = 'declare_method';
ExtDirect::$authorization_function = 'authorize';

function transform_result( $action, $result )
{
if ( $action->form_handler )
$result = array( 'success' => $result );

return $result;
// return modified result
}

function transform_response( $action, $response )
{
$response['error_msg'] = MyFramework::$errors;
$response['success_msg'] = MyFramework::$success;
return $response;
// return modified response
}

function declare_method( $class, $method )
{
$key = $class . '::' . $method;
return in_array( $key, MyFramework::$user->permissions );
// return boolean - declare the method in the API or not
}

function authorize( $action )
{
return declare_method( $action->action, $action->method );
// return boolean - authorize the action call or not
}

// Types of the parameters received by the functions are:
// $action - ExtDirectAction
// $result - array
// $response - array
// $class - string
// $method - string



Feedback is appreciated.

Thanks!

ttbgwt
14 Oct 2010, 12:26 PM
Excellent Job! This is a great Ext.Direct implementation. Has there been any updates to this since August?

ttbgwt
15 Oct 2010, 8:00 PM
Do you have an example on how to configure and load a DirectStore using your implementation?

j.bruni
26 Oct 2010, 9:27 AM
Excellent Job! This is a great Ext.Direct implementation. Has there been any updates to this since August?

No updates since August. This is currently the latest version. (Today is October 26, 2010.)

j.bruni
26 Oct 2010, 9:32 AM
Do you have an example on how to configure and load a DirectStore using your implementation?

I have developed a PHP / MySQL application (a basic CMS) making intensive use of this implementation. In the future, when this CMS is more mature, I want to open source it too.

Several components data are loaded using DirectStore (treepanel, treegrid, grid, list, etc.) - in the server side, the data source is MySQL (or Memcached).

I do not have a simple/small sample to post, but if you specify more details about what do you want, I may help.

padawan
31 Oct 2010, 5:19 AM
Just want to say thank you for sharing this. Although I have been using extjs for some time now, I am far from being an expert. But anywayz, I think this Ext.Direct implementation is great!

ttbgwt
9 Nov 2010, 4:40 PM
Do you have an example of how to use the authorize function? And how to send username and password encrypted to it?

walldorff
14 Dec 2010, 11:50 PM
In the form I have the api config option set to 2 functions: load (which successfully load a string into the field) and submit (where I get an error from my custom class, complaining about an empty object in the parameter).

Demo: http://extme.eu/DirectTest/



// the body part of the index.php
<body>
<script type="text/javascript" src="Numbers.php?javascript"></script>
<script type="text/javascript" src="form.js"></script>
</body>




<?php
// the class
require 'ExtDirect.php';

class Numbers {
public function validate($data) {
// $_POST is empty!
// error: 'Cannot use object of type stdClass as array'
$num = $data['num'];
$response = array('success' => true);
if (!is_numeric($num)) {
$response['success'] = false;
$response['errors'] = array(
'num'=>'Not a valid number.'
);
}
return $response;
}

public function load($id) {
return 'loaded: '.$id;
}
}

ExtDirect::provide('Numbers');
ExtDirect::$form_handlers = array( 'Numbers::validate');
?>




// form.js
Ext.ns('Ext.php');
Ext.onReady(function () {
Ext.QuickTips.init();

Ext.php.callback = function (response, e) {
if (true === e.status) {
Ext.php.formExample.items.items[0].setValue(response);
}
};

Ext.php.formExample = new Ext.form.FormPanel({
title: 'Validation Form'
,monitorValid: true
,renderTo: Ext.getBody()
,padding: 10
,height:120
,width:300
,buttons:[{
text: 'Validate'
,handler: function(){
Ext.php.formExample.getForm().submit();
}
}]
,items: [{
fieldLabel: 'Enter a number'
,xtype: 'textfield'
,name: 'num' // also tried with id: 'num'
,msgTarget: 'side'
,allowBlank:false
}]
,api: {
load: Ext.php.Numbers.load('loadtest', Ext.php.callback)
,submit: Ext.php.Numbers.validate
}
});
});


Obviously the class receives an empty array; where does that come from? Even the $_POST array is empty.
In get_request_actions() (line 274) the foreach tries to access $rpc->$variable. Is this correct?

$$variable = ( isset( $rpc->$variable ) ? $rpc->$variable : '' );

I tried with ExtDirect::$default_api_output = "javascript", ExtDirect::$instantiate_static = true, ExtDirect::$include_inherited_methods = true and almost every other config setting.
I would be grateful for a kick in the right direction.

Andrew Peacock
15 Dec 2010, 8:34 AM
// $_POST is empty!
// error: 'Cannot use object of type stdClass as array'

Doesn't that mean that you should be using:


$num = $data->num;

?

Regards,
Andy

walldorff
15 Dec 2010, 2:34 PM
Doesn't that mean that you should be using:
$num = $data->num;
Thanks Andy. If you run the demo with Firebug, you'll notice that the data is an empty object. The PHP class method should get a $_POST as an array, but also this is empty. Its puzzling. It's a simple form, it should submit simple data, only one field. In other test environment this form runs ok.

Andrew Peacock
22 Dec 2010, 5:34 AM
OK, you got me stumped so I had a play around.

Change:



ExtDirect::provide('Numbers');
ExtDirect::$form_handlers = array( 'Numbers::validate');


to



ExtDirect::$form_handlers = array( 'Numbers::validate');
ExtDirect::provide('Numbers');


I think the provide has to go last!

Also, check the comments here about "receiving parameters":
http://www.sencha.com/forum/showthread.php?102357-Extremely-Easy-Ext.Direct-integration-with-PHP&p=484078#post484078

This means your PHP function definition would be:


public function validate($num) {

Hope that helps!
Andy

walldorff
28 Dec 2010, 12:59 AM
Andy, that was it!
Your suggestion to switch the call to the ExtDirect methods did the trick.
The call to ExtDirect::provide() apparently has to be the last call in the sequence, which seems perfectly logical. I can't believe I missed that.

Thank you so much for your help!

Roland

Andrew Peacock
28 Dec 2010, 1:19 AM
No problem! Glad to help
Andy

tiagoadp
4 Apr 2011, 11:51 PM
Hello, are you planning any updates for ext 4.0? :-?

I tryed to solve the problems by myself on the "PR" and now on "BETA 1", but i couldĀ“t do it...

Any update will be most wellcome...

hschaefer123
2 May 2011, 9:05 AM
Hi J.Bruni,
i love your Ext.Direct implementation.

After playing around with SAKI's Ext Direct HTTP State Provider and realizing the Implementation of
examples/state/SessionProvider.js

I figured out, that this kind of implementation perfectly fits into your Direct Implementation.
That means, your implementation will also be able to save and restore component ui state information.

To make this working i added i modified the javascript api


/**
* @return string JSON encoded array containing the full API declaration
*/
static public function get_api_javascript()
{
session_start();
//unset($_SESSION['extjsstate']);
if(!isset($_SESSION['extjsstate'])){
$_SESSION['extjsstate'] = array(
'sessionId'=>session_id()
);
}

$template = <<<JAVASCRIPT

Ext.namespace( '[%namespace%]' );
[%descriptor%] = [%actions%];
Ext.Direct.addProvider( [%descriptor%] );
Ext.appState = [%appstate%];

JAVASCRIPT;

$elements = array(
'[%actions%]' => self::get_api_json(),
'[%namespace%]' => ExtDirect::$namespace,
'[%descriptor%]' => ExtDirect::$descriptor,
'[%appstate%]' => json_encode($_SESSION['extjsstate'])
);

return strtr( $template, $elements );
}



i also added some code at the end of
protected function get_request_actions()


// handle state
session_start();
if(!isset($_SESSION['extjsstate'])){
$_SESSION['extjsstate'] = array(
'sessionId'=>session_id()
);
}
foreach($_COOKIE as $name=>$value){
// look for state cookies
if(strpos($name, 'ys-') === 0){
// store in session
$_SESSION['extjsstate'][substr($name, 3)] = urlencode($value);
// remove cookie
setCookie($name, '', time()-10000, '/');
}
}


and that's it.

The only thing to do is to initialize StateManager onReady


Ext.onReady(function() {
...
Ext.state.Manager.setProvider(
new Ext.state.SessionProvider({state: Ext.appState})
);
... init code


Now the API include contains a new variable holding the state info
Ext.appState = {"sessionId":"9e7f3v1vbcrf5r45j3hb28g8p2","MyDirectoryGrid":"o%3Acolumns%3Da%25...."};

Now if something is changed inside ui, the next roundtrip through Ext.Driret Router the UI State will be catched from COOKIE and stores inside PHP Session (Serverside instead of Client Side implementation).

My kind of implementation is only for demo/testing. Maybe you can implement such feature via new toogle Property and callBack function (like auth) to make implementation of individually store containers for session data possible (maybe mySQL or SQLite).

I think, that would be a great addon to your fantastic implementation.

Cheers,
Holger

JorisA
4 May 2011, 1:34 PM
Are there any plans to make this compatible with Ext4?

AndreKR
25 May 2011, 7:53 PM
What do I need to set count_only_required_params to, so that optional parameters are optional?

j.bruni
26 May 2011, 1:25 AM
I've been extremely far far away from this forum for several months. But now I'm here!

@Andrew Peacock: Thank you very much for the support!

@walldorff: Yes, ExtDirect::provide() needs to be the last call. The only documentation we currently have is this thread... specially the first posts. Take a look!

@tiagoadp & JorisA: I have not used ExtJs 4 yet. Isn't it compatible? Have someone tried? Which are the errors? Anyway, it sounds like a good idea to make it ExtJs 4 compatible, if it isn't. Just added to my "to do" list.

@hschaefer123: State Provider integration? Sounds very nice! I'm sorry I have not taken the time to take care of your request yet.

@padawan & hschaefer123: Thanks! Every praise really motivates us! :)

@ttbgwt: Your request is so old now... are you still interested? The answer would go beyond the strict scope of this Ext.Direct integration.

Finally, @AndreKR...


What do I need to set count_only_required_params to, so that optional parameters are optional?

This post (http://www.sencha.com/forum/showthread.php?102357-Extremely-Easy-Ext.Direct-integration-with-PHP&p=480214&viewfull=1#post480214) (http://www.sencha.com/forum/showthread.php?102357-Extremely-Easy-Ext.Direct-integration-with-PHP&p=480214&viewfull=1#post480214) teaches how apply configurations (I know you read it, I am linking for the others...) ;)

All configurations must come before the "ExtDirect::provide" call:


ExtDirect::$configuration_name = $value;In your case, you want to perform the following configuration:


ExtDirect::$count_only_required_params = true;This will make the "len" property of the respective API declaration correspond to the number of required parameters. So, for a PHP method declared like this...


function calculate_something( $needed, $optional = 9 )...the "len" value will be "1", instead of "2".

And this is all.

Now, ExtJS requires you to make the remote function call with the number of parameters declared in the "len" property. In other words: there is no such things as "optional parameters" in the client-side, because of the way ExtJS Direct is designed.

In the example, you need to choose: it will be either "1" or "2" parameters. You simply can't choose "1" and optionally "2", at the ExtJS side. Makes sense?

JorisA
29 May 2011, 1:21 AM
Guess I should have bothered to try before I ask, so far it's been working great with Ext4, haven't run into any problems yet :)

By the way, what about supporting custom events like these:
http://www.sencha.com/forum/showthread.php?111101-Ext.Direct-Exceptions-and-Events&highlight=login

I think the ability to allow hooking into direct calls or even complete batches could make user or rights checking a lot easier.

j.bruni
30 May 2011, 8:28 AM
Guess I should have bothered to try before I ask, so far it's been working great with Ext4, haven't run into any problems yet :)

Great! I will trust you and announce "Compatible with ExtJs 4" at the opening post! :)


By the way, what about supporting custom events like these:
http://www.sencha.com/forum/showthread.php?111101-Ext.Direct-Exceptions-and-Events&highlight=login

Check transform_response_function configuration, here: http://www.sencha.com/forum/showthread.php?102357-Extremely-Easy-Ext.Direct-integration-with-PHP&p=500607&viewfull=1#post500607


I think the ability to allow hooking into direct calls or even complete batches could make user or rights checking a lot easier.

User and permission rules must be performed at server-side! At the client-side, we can have this checking also, but with the sole purpose of adjusting the User Interface accordingly. Right?

At the server-side, I use the authorization_function configuration to grant or deny the execution of the request.


ExtDirect::$authorization_function = 'authorize';

function authorize( string $class, string $method )
{
if ( user_is_allowed_to_perform_this_action( $class . '::' . $method ) )
return true;
else
return false;
}

j.bruni
30 May 2011, 8:45 AM
By the way, what about supporting custom events like these:
http://www.sencha.com/forum/showthread.php?111101-Ext.Direct-Exceptions-and-Events&highlight=login

It is possible to fire custom events from server-side by using the transform_response configuration. See http://www.sencha.com/forum/showthread.php?102357-Extremely-Easy-Ext.Direct-integration-with-PHP&p=500607&viewfull=1#post500607

For example:


ExtDirect::$transform_response = 'transform_response';

function transform_response( $action, $response )
{
// Types of the parameters received by this function:
// $action - ExtDirectAction
// $response - array

if ( $some_condition )
{
$response['type'] = 'event';
$response['name'] = 'trace';
$response['where'] = 'Where this YYYY happen';
$response['trace'] = 'Some data';
}

return $response;
// return modified response
}

The key is to change the $response['type'], which most affect the ExtJS receiving of the message at the client-side.

jmefford
15 Jun 2011, 1:03 PM
I really love this implementation of Ext Direct. Thanks for all of the work you have put into this. I noticed on the video about 6 months ago they said that Ext.Direct allowed for long running processes to be specified separately so that they return on their own time. Is there a way to do this with the current implementation? I have a dashboard with several things loading, and some of the reports take about 7-10 seconds, so i want those to return on their own time, and not hold up the rest of the batch.

Any ideas?

j.bruni
16 Jun 2011, 3:33 AM
I really love this implementation of Ext Direct. Thanks for all of the work you have put into this. I noticed on the video about 6 months ago they said that Ext.Direct allowed for long running processes to be specified separately so that they return on their own time. Is there a way to do this with the current implementation? I have a dashboard with several things loading, and some of the reports take about 7-10 seconds, so i want those to return on their own time, and not hold up the rest of the batch.

Any ideas?

I have two ideas. I don't know if there is a better way, and I would also be happy to know.

1) Create two distinct remote providers. One of them would handle the long running processes only. The other one would handle the other processes. To do this, you need to create two PHP files, one for each API. In this case, you need to include both files using the <script> tag. Calls to one provider do not get mixed with the calls to the other provider.

2) Set enableBuffer to false. See Ext.Direct.RemotingProvider documentation. This setting allows / disallows to send multiple method calls in a single request. If you set it to false, each method will be called through its separated HTTP request. You may want to mix this idea with the previous one, by setting enableBuffer to false only to the long running processes provider, and leaving the default value to the other provider.

Problem is... you don't have a way to change enableBuffer value with the current implementation.

Solution is... we can implement it.

Below line #41 (static public $id = '';), add:



/**
* @var boolean|integer Ext.Direct Provider attribute "enableBuffer"
*/
static public $enableBuffer = 10;
And change the $api_array variable declaration, adding the enableBuffer key, like this:



$api_array = array(
'id' => self::$id,
'url' => ( empty( self::$url ) ? $_SERVER['PHP_SELF'] : self::$url ),
'type' => 'remoting',
'namespace' => self::$namespace,
'descriptor' => self::$descriptor,
'enableBuffer' => self::$enableBuffer
);
Now, in your code, you should be able to set enableBuffer to false like this:


ExtDirect::$enableBuffer = false;

This line must come, as all other configurations, before the call to ExtDirect::provide.

I hope this helps... What do you think?

jmefford
16 Jun 2011, 6:28 AM
Thanks for the quick reply. I think that this will work. I am still trying to decide whether to use the enableBuffer on my set of long running ones (they are all connecting to our exchange server to pull data) because they all return at the same time, so i may just do two different providers. One for our database driven reports, and one for exchange server. However, knowing the option to set the enable buffer will come in handy later if someone desires a very complex report on the dashboard.

j.bruni
23 Jun 2011, 9:48 AM
Today is "Extremely Easy Ext.Direct integration with PHP" birthday!

This thread was opened exactly one year ago, in June 23rd, 2010.

Congratulations!!! =D>

Thanks everybody for the comments, questions, reports, suggestions, praises, support.
Thanks also to the whole ExtJS team, past, present and future.
And I also want to thank the open source software community.
And, why not? Thanks to my father, mother, wife, kids, pets, nature and God.

walldorff
23 Jun 2011, 9:53 AM
And thank you for your excellent work! :)

jessec10
23 Jun 2011, 10:11 PM
Wouldn't it be nice if this code would have it's own website where all the documentation could be organised.

zoq
30 Jun 2011, 9:37 AM
Wouldn't it be nice if this code would have it's own website where all the documentation could be organised.

+1 !

a simple bitbucket/github whatever site would come in handy with no cost involved.
I think I will never understand why people prefer to spend hours diggin' in a forum and going back and forth to collect and build up their own, subjective little documentation (which differs avg. 25% from those made by others ..) :-?

avishnev
29 Jul 2011, 11:50 AM
Hello, i downloaded ExtDirect.zip and unzipped it into my folder. While I don't get any errors on the Developer Web Inspector window in my browser (Safari, Chrome). The results from the date function is not being displayed. What is the next step? What is the next step? Where should I look? do I need to open additional ports (other then 80) in my firewall?

j.bruni
4 Aug 2011, 9:29 PM
Hello, i downloaded ExtDirect.zip and unzipped it into my folder. While I don't get any errors on the Developer Web Inspector window in my browser (Safari, Chrome). The results from the date function is not being displayed. What is the next step? What is the next step? Where should I look? do I need to open additional ports (other then 80) in my firewall?

Do you have PHP and a web server (Apache or Nginx or similar) installed and properly configured? Can you run PHP scripts on your machine? You need to be able to run PHP scripts from your folder, and to see the results in your browser.

Blob
20 Aug 2011, 12:32 PM
Does not work with ExtJS 4. Here is an updated version of example.html:


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Ext.Direct Example</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="http://extjs.cachefly.net/ext-4.0.2a/ext.js"></script>
<script type="text/javascript" src="example.php?javascript"></script>
<script type="text/javascript">
Ext.onReady( function() { Ext.php.Server.date( 'Y-m-d', function(result){ alert( 'Server date is ' + result ); } ); } );
</script>
</head>
<body>
<h1>Ext.Direct Example</h1>
</body>
</html>


And here is an error log from Chrome console:


example.html:4 Uncaught TypeError: Cannot call method 'addProvider' of undefined
example.html:13 Uncaught TypeError: Cannot call method 'date' of undefined

j.bruni
21 Aug 2011, 3:25 AM
Does not work with ExtJS 4.


And here is an error log from Chrome console:


example.html:4 Uncaught TypeError: Cannot call method 'addProvider' of undefined
example.html:13 Uncaught TypeError: Cannot call method 'date' of undefined

Hi, Blob,

If you use ext-all.js instead of ext.js, it works.


http://extjs.cachefly.net/ext-4.0.2a/ext-all.js

If you want to use ext.js, then you need to require the Ext.direct.Manager class:


Ext.syncRequire( 'Ext.direct.Manager' );

This line of code must be before the Direct API is loaded, like this:


<script type="text/javascript" src="http://extjs.cachefly.net/ext-4.0.2a/ext.js"></script>
<script type="text/javascript">Ext.syncRequire( 'Ext.direct.Manager' );</script>
<script type="text/javascript" src="example.php?javascript"></script>


I have already updated ExtDirect.php by changing the ExtDirect class. I have not published this new version yet, because there are a few other modifications. The change is in the beginning of the "get_javascript_api" method:


/**
* @return string JSON encoded array containing the full API declaration
*/
static public function get_api_javascript()
{
$template = <<<JAVASCRIPT

if ( Ext.syncRequire )
Ext.syncRequire( 'Ext.direct.Manager' );

Ext.namespace( '[%namespace%]' );
[%descriptor%] = [%actions%];
Ext.Direct.addProvider( [%descriptor%] );

JAVASCRIPT;

This is backwards compatible and does the job of requiring Ext.direct.Manager in ExtJS 4.

JavascriptParrot
4 Nov 2011, 3:36 AM
hi all,

I try to implement this nice class with our framework but I need always a parameter in de registered class.
I mean this:



<?php

require 'ExtDirect.php';

class Server
{

function construct__(frame $frame){
$this->_frame = $frame;
}

public function date( $format )
{
return date( $format );
}
}

ExtDirect::provide( 'Server' );

?>



It results in the follow error

Warning: Missing argument 1 for TestClient::__construct(), called in ExtDirect.php on line 501

I try to change some things but I am stuck, maybe someone could give me some useful tips?

Thanks,
Freez

j.bruni
6 Nov 2011, 2:44 AM
I try to change some things but I am stuck, maybe someone could give me some useful tips?


Hi, Freez,

The easiest workaround would be to provide a default value for the constructor parameter:



<?php

require 'ExtDirect.php';

class Server
{

function construct__(frame $frame = 'default'){
$this->_frame = $frame;
}

public function date( $format )
{
return date( $format );
}
}

ExtDirect::provide( 'Server' );

?>





In fact, this ExtDirect implementation does not send parameters to the class constructor. The only exception is when ExtDirect::$constructor_send_params config is set to true. But, in this case, the action parameters (coming from the client-side) are sent to the class constructor.

Anyway, I am open to suggestions on how to provide parameters to the constructor from the server-side... What do you think? Should a new configuration option be created for this? Something like ExtDirect::$constructor_params ? How would it work?

Maybe like this:



<?php

require 'ExtDirect.php';

class Server
{

function construct__(frame $frame){
$this->_frame = $frame;
}

public function date( $format )
{
return date( $format );
}
}

$params = array( 'this is the value for "frame" parameter' );
ExtDirect::$constructor_params = array( 'Server' => $params );
ExtDirect::provide( 'Server' );

?>



What do you think?

JavascriptParrot
6 Nov 2011, 11:00 PM
Hello!

That sounds very good :))! Now I has make a little hack but it always send the constructor parameter.
If you would implement this you are brilliant!



$params = array( 'this is the value for "frame" parameter' );
ExtDirect::$constructor_params = array( 'Server' => $params );


Thanks,
Freez

j.bruni
8 Nov 2011, 7:00 AM
That sounds very good :))!

Cool! So, hold on. I am working on it. A new version will be released very soon.

j.bruni
8 Nov 2011, 5:25 PM
Hi, everybody!

I am extremely happy to announce a new version for this extremely easy to use integration of PHP with ExtJS.

You can download it from the opening post of this thread (http://www.sencha.com/forum/showthread.php?102357-Extremely-Easy-Ext.Direct-integration-with-PHP&p=480079&viewfull=1#post480079).

We do not have many changes. In fact, there are three:

1) Improved compatibility with ExtJS 4: now the JavaScript code verifies if the required ExtJS class for Direct is loaded, and load it if it is not.

2) Comments were revised, fixed and improved. We had a few "documentation bugs" in the comments. Now they are gone. I think I have corrected all of them.

3) The main change is the new constructor_params configuration, a feature recently requested. See how to use it in the example below:



<?php

require 'ExtDirect.php';

class Server
{
public function __construct( $pretext )
{
$this->pretext = $pretext;
}

public function date( $format )
{
return $this->pretext . date( $format );
}
}

ExtDirect::$constructor_params = array(
'Server' => array( 'The current server date is ' )
);

ExtDirect::provide( 'Server' );

?>

In the example above, an instance of the "Server" class is created when an action is requested by ExtJS from the client-side. If you need to pass a parameter to the class constructor when this instance is created, now you can!

The constructor_params configuration must be an array, where the keys are the class names and the values are the parameters to be passed to the corresponding constructor (it must be an array also).

JavascriptParrot
11 Nov 2011, 2:07 AM
Thank you! I will test it asap! =D>=D>=D>

demithron
11 Jan 2012, 2:02 AM
Nice class man, but as i can see parameter enableBuffer again absent in new release.
can u back it in next updates?
thanks in advance.

lsdriscoll
12 Jan 2012, 4:42 AM
Hi j.bruni!

First of all, thanks so much for this package! It's saved so much time.

Second, I have been using it with the charts module and have uncovered a potential problem :
The JSON data always returns table data as strings from the proxy, even if it's marked as an integer in the db. Obviously this broke the charts. I have seen many people on forums having the issue that their charts wouldn't work using a direct proxy, so I'm guessing that this might be an Ext problem instead of relating to your code.

Anyway, as a temporary fix I modified lines 621 & 623 so that the json_encode functions included the JSON_NUMERIC_CHECK flag.

Of course, this isn't ideal as it will force ALL number values to be send as integers, when a string may actually be desired.

Was wondering what your thought were on this?

j.bruni
22 Jan 2012, 4:06 AM
Nice class man, but as i can see parameter enableBuffer again absent in new release.
can u back it in next updates?
thanks in advance.

Since enableBuffer parameter has never been available, there is no way to bring it back. :)

I thought on creating some caching mechanism but never implemented it. IMO, the "cost/benefit" does not worth. It is the first time someone requests it. (If I understood correctly what you meant.)

j.bruni
22 Jan 2012, 5:44 AM
Second, I have been using it with the charts module and have uncovered a potential problem :
The JSON data always returns table data as strings from the proxy, even if it's marked as an integer in the db. Obviously this broke the charts. I have seen many people on forums having the issue that their charts wouldn't work using a direct proxy, so I'm guessing that this might be an Ext problem instead of relating to your code.

Hi! Thanks for your feedback! Sorry for the delay on my reply. Anyway, here are my thoughts on this:

In my point of view, the "potential problem" is not exactly an Ext/JSON issue, but a database issue.

Every data returned from SQL queries is always of string type. The only exception is the null value, which returns as null. Everything else is string, it does not matter the type of the data in the database. A date column, or an integer column... the database query results always arrive in the PHP side as strings.

Also, consider that we can satisfy Ext Data Charts needs of integer values, but will not be able to send JavaScript Date objects, for example, using pure JSON, without any extra processing at the client-side...

So, my approach is: in my server-side app code, I use an intermediary "parse" function which transforms each row of the database result into something ready-to-use for the client side. Something like:


function parse_rows($original_result)
{
$parsed_result = array();
foreach($original_result as $row)
{
$row['quantity'] = intval($row['quantity']);
$row['price'] = floatval($row['price']);
$parsed_result[] = $row;
}
return $parsed_result;
}

The important thing here is that we need to transform the database-returned string into its proper integer data-type. In fact, I use this "parse" function to apply other kinds of transformations (for example, joining "first_name" with "last_name" to return a "name" column which does not even exist in the database).

I think this is where the solution should be focused... not at client-side or the JSON encoding. This is my opinion.

lsdriscoll
22 Jan 2012, 8:59 AM
The important thing here is that we need to transform the database-returned string into its proper integer data-type. In fact, I use this "parse" function to apply other kinds of transformations (for example, joining "first_name" with "last_name" to return a "name" column which does not even exist in the database).

I think this is where the solution should be focused... not at client-side or the JSON encoding. This is my opinion.

Hi, thanks for the response!

I completely agree that the solution should be at the server side.

However in terms of your solution, consider this situation:
You have an application that draws data from multiple tables that are designed to provide data 'as is' with no custom processing required. Of course, these tables all have varying column names.
To avoid code replication you have built a generic crud interface to handle these tables and wish to use this interface in future projects.

In this situation, processing strings into integers on a per table basis is not viable as the read method will not know which columns to convert. The only place the data types are defined will be in the ext models themselves.

In my opinion, it would make sense for the data types to be sent along with request and used by the read method to determine how to process the rows.

Of course, the drawback is that this would require overriding the ext core (unless I'm missing something vital).

I look forward to hearing your thoughts on this, no doubt to tell me where I've gone wrong ;)

Lee

dschere
14 Mar 2012, 7:42 AM
// so I'm inside either onReady or launch if I'm using
// MVC

// I do this

var provider = new Ext.direct.PollingProvider({
type:'remoting',
url: <The path to your php script>
});

Ext.direct.Manager.addProvider( provider );

alert( Server.date() );

// would this work?

DeFacto
29 Apr 2012, 2:19 AM
alert( Server.date() );

// would this work?

No. Because alert is called immediately, but Server.date() makes ajax request, that's way you need to use callback function


Ext.php.Server.date( 'Y-m-d', function(result){ alert( 'Server date is ' + result );
} ); } );

khebs@live.com
26 May 2012, 5:43 AM
Hi Mate, been so long :) can you make a laravel bundle for this? please ;)

stewardsencha
26 May 2012, 7:49 AM
1. Create folder laravel/bundles

2. Copy Bruni's ExtDirect.php into that folder

3. Create laravel/bundles/direct/start.php:



require_once(Bundle::path('direct').'ExtDirect.php'); // J Bruni


4. Add it to your application/bundles.php



return array(
'direct'=>array('handles'=>'direct')
);


Start routing to your direct bundle !

In other words, there's nothing for Bruni to do. His work is awesome.
Just follow the simple recipe at laravel for creating bundles. Ask for help at that site.
Or don't bother. It's just as easy to use as a library.

You have to read his code. Everything is there. But it will need to be tweaked for your situation.

His controller does not return to the laravel core, he simply spits out the results when ready.

Laravel dudes would frown. Whether you need to take that further depends again on your situation.

But you know what? I am finding ExtDirect is sucking away my life for no fun.

It creates challenges in the build and with using SA. It is always wanting extra attention for this or that.

For me, it has slowed progress considerably, and I regret using it.

Just look at all of bruni's code (which is now your own to understand and manage).

You'll need to discover how to use it with SA. Thats sucks away a bit.

And getting it right (do not try to execute before Ext is loaded, delaying creation of stores until provider is ready) may be no brainer for the experienced dudes, but it tripped me many times.

And for what exactly? Know your use cases and motivation. Plan on extra delays for ExtDirect.

Here (was) my laravel/bundles/direct/routes.php as I built the examples.
This was Day One code, I have since moved on...



///////////////////////////////////////////////////////////////////////////
/*
* These first two routes implement JBruni's simple example.
*
* site.com/direct/example (JBruni would have used site.com/example.php, we drop the php extension)
* site.com/direct/example.html (You need to edit direct/example.html to point to your ext install)
*/
Route::any('direct/example',function() {
class Server
{
public function date( $format )
{
return date( $format );
}
}
ExtDirect::$url=URL::to('direct/example');
ExtDirect::provide( 'Server' ); // poof our Server class is available to javascript
});

Route::get('direct/example.html',function(){
return Response::make(file_get_contents(Bundle::path('direct').'example.html'));
});

/*
* Hmm. Now we need a page for the extjs samples.
*
* ExtAsset::sample takes an ordered list of scripts to add to direct/view/sample.php
* That should give us a shell to test a couple of the ExtDirect sampless
*/

/////////////////////////////////////
Route::any('direct/test',function() {
ExtDirect::$url=URL::to('direct/test');
ExtDirect::provide( 'TestAction' );
});
Route::any('direct/test.html',function() {
ExtDirect::$url=URL::to('direct/test');
ExtAsset::sample(array('direct/test?javascript','apps/direct/test.js'));
return View::make('direct::sample')->with('title', 'test');
});


/////////////////////////////////////////
// Service the polling demo. No class.
Route::get('(:bundle)/poll', function() {

$result=array(
'type'=>'event',
'name'=>'message',
'data'=>'Successfully polled at: '. date('g:i:s a')
);
return new Response(json_encode($result),200, array('Content-Type'=>'application/json'));
});
Route::any('direct/poll.html',function() {
ExtDirect::$url=URL::to('direct/poll');
ExtAsset::sample(array('apps/direct/poll.js'));
return View::make('direct::sample')->with('title', 'poll');
});

// Now we have a pattern, follow it for the other samples: grid, tree and named-arguments...
Route::any('direct/tree',function() {
IoC::resolve('TestAction');
ExtDirect::$url=URL::to('direct/tree');
ExtDirect::provide( 'TestAction' );
});
Route::any('direct/tree.html',function() {
ExtDirect::$url=URL::to('direct/tree');
ExtAsset::sample(array('direct/tree?javascript','apps/direct/direct-tree.js'));
return View::make('direct::sample')->with('title', 'tree');
});
//
Route::any('direct/grid',function() {
IoC::resolve('TestAction');
ExtDirect::$url=URL::to('direct/grid');
ExtDirect::provide( 'TestAction' );
});
Route::any('direct/grid.html',function() {
ExtDirect::$url=URL::to('direct/grid');
ExtAsset::sample(array('direct/grid?javascript','apps/direct/direct-grid.js'));
return View::make('direct::sample')->with('title', 'grid');
});
//
Route::any('direct/named-arguments',function() {
ExtDirect::$url=URL::to('direct/named-arguments');
ExtDirect::provide( 'TestAction' );
});
Route::any('direct/named-arguments.html',function() {
ExtDirect::$url=URL::to('direct/named-arguments');
ExtAsset::sample(array('direct/named-arguments?javascript','apps/direct/named-arguments.js'));
return View::make('direct::sample')->with('title', 'named-arguments');
});


If I had to do this again:
- I would not bother making ExtDirect a bundle. Just drop it in your library and use it.
- I would not use ExtDirect without a serious motivation ;)

Good luck.

And many thanks to J Bruni.

khebs@live.com
26 May 2012, 11:22 AM
Hi Thanks for the detailed explanations :) this is actually a debate to my self that i would just use simple api for this.. but ill try around see what fits..

Kynao
29 Aug 2012, 8:11 AM
Hello,

there is no simple way or documentation to use it with sencha architect ?
what alternative to ext.direct for using extjs with laravel along a simple approach ?

stewardsencha
29 Aug 2012, 8:29 AM
Like I say, I have moved on, but this is what worked for me at the time:



// There are supposed to be formats attached to the request, eg ?xml ?json.
// Thought ExtDirect handled it and thought I saw it working once. But this is okay for now.
Route::any('direct/video, direct/arkvideo', function() {

try{
// Laravel has sexier ways to do this I am sure.
if (strstr(URI::current(),'ark'))
{
// Request is coming from the Sencha Architect (ark ark)
ExtDirect::$default_api_output = 'json';
}
else
{
// Service for ext apps
ExtDirect::$default_api_output = 'javascript';
}



In other words, I gave SA a slightly different url for ExtDirect, so laravel could notice and set the output format accordingly.

Cheesy but easy.

Kynao
29 Aug 2012, 8:40 AM
Thanks you stewardsencha (http://www.sencha.com/forum/member.php?181925-stewardsencha)
http://www.sencha.com/forum/images/statusicon/user-online.png

Kynao
29 Aug 2012, 10:54 AM
Like I say, I have moved on

By the way, you moved on what ? :)
About "You'll need to discover how to use it with SA". Did someone expose the created challenges in the build and with using SA ? and discovered a solution how to deal with it.

j.bruni
30 Aug 2012, 1:37 AM
This ExtDirect class does provide a shortcut to select between JSON or JavaScript output, which is to provide a "javascript" parameter in the query string of the requested URL. For example:

http://domain.com/direct/example - this outputs JSON

http://domain.com/direct/example?javascript - this outputs JavaScript

This is performed at ExtDirectController::get_api method, as seen below:



/**
* @return string JSON or JavaScript API declaration for the classes on "api_classes" configuration array
*/
public function get_api()
{
if ( isset( $_GET['javascript'] ) || ( ExtDirect::$default_api_output == 'javascript' ) )
return ExtDirect::get_api_javascript();
else
return ExtDirect::get_api_json();
}


The "core" of it is the isset( $_GET['javascript'] ) snippet.

Kynao
30 Aug 2012, 10:04 AM
Thanks again j.bruni (http://www.sencha.com/forum/member.php?77960-j.bruni)

cbj4074
30 Oct 2012, 11:11 AM
What do I need to set count_only_required_params to, so that optional parameters are optional?

I am wondering the same thing, and if the response that follows is still relevant.



...
Finally, @AndreKR...

This post (http://www.sencha.com/forum/showthread.php?102357-Extremely-Easy-Ext.Direct-integration-with-PHP&p=480214&viewfull=1#post480214) (http://www.sencha.com/forum/showthread.php?102357-Extremely-Easy-Ext.Direct-integration-with-PHP&p=480214&viewfull=1#post480214) teaches how apply configurations (I know you read it, I am linking for the others...) ;)

All configurations must come before the "ExtDirect::provide" call:


ExtDirect::$configuration_name = $value;In your case, you want to perform the following configuration:


ExtDirect::$count_only_required_params = true;This will make the "len" property of the respective API declaration correspond to the number of required parameters. So, for a PHP method declared like this...


function calculate_something( $needed, $optional = 9 )...the "len" value will be "1", instead of "2".

And this is all.

Now, ExtJS requires you to make the remote function call with the number of parameters declared in the "len" property. In other words: there is no such things as "optional parameters" in the client-side, because of the way ExtJS Direct is designed.

In the example, you need to choose: it will be either "1" or "2" parameters. You simply can't choose "1" and optionally "2", at the ExtJS side. Makes sense?

I am using the sample descriptor code (for PHP) that is provided with the Ext Direct Pack and the mechanism for setting default configuration values seems to have changed:



$api = new ExtDirect_API();

$api->setRouterUrl('router.php'); // default
$api->setCacheProvider($cache);
$api->setNamespace('Ext.ss');
$api->setDescriptor('Ext.ss.APIDesc');
$api->setDefaults(array(
'autoInclude' => true,
'basePath' => 'classes'
));


Regarding ExtJS Direct configuration, in general, I am wondering if these configuration directives are still relevant:


Easy.

If the configuration option name is "configuration_name" and the configuration value is $value, use this syntax:


ExtDirect::$configuration_name = $value;And that's all.

Now, let's see the available configuration options:

- name: api_classes
- type: array of strings
- meaning: Name of the classes to be published to the Ext.Direct API
- default: empty
- comments: This option is overriden if you provide a non-empty $api_classes parameter for the "ExtDirect::provide" method. Choose one or another. If you want to declare a single class, you can set $api_classes as a string, instead of an array.
- example
ExtDirect::$api_classes = array( 'Server', 'OtherClass', 'StoreProvider' );- name: url
- type: string
- meaning: Ext.Direct API attribute "url"
- default: $_SERVER['PHP_SELF']
- comments: Sometimes, PHP_SELF is not what we want. So, it is possible to specify the API URL manually.
- example
ExtDirect::$url = '/path/to/my_php_script.php';- name: namespace
- type: string
- meaning: Ext.Direct API attribute "namespace"
- default: "Ext.php"
- comments: Feel free to choose your own namespace, according to ExtJS rules for it.
- example
ExtDirect::$namespace = 'Ext.Dharma';- name: descriptor
- type: string
- meaning: Ext.Direct API attribute "descriptor"
- default: "Ext.php.REMOTING_API"
- comments: Feel free to choose your own descriptor, according to ExtJS rules for it, and to the chosen namespace.
- example
ExtDirect::$descriptor = 'Ext.Dharma.REMOTING_API';- name: count_only_required_params
- type: boolean
- meaning: Set this to true to count only the required parameters of a method for the API "len" attribute
- default: false
- example
ExtDirect::$count_only_required_params = true;- name: include_static_methods
- type: boolean
- meaning: Set this to true to include static methods in the API declaration
- default: false
- example
ExtDirect::$include_static_methods = true;- name: include_inherited_methods
- type: boolean
- meaning: Set this to true to include inherited methods in the API declaration
- default: false
- example
ExtDirect::$include_inherited_methods = true;- name: instantiate_static
- type: boolean
- meaning: Set this to true to create an object instance of a class even if the method being called is static
- default: false
- example
ExtDirect::$instantiate_static = true;- name: constructor_send_params
- type: boolean
- meaning: Set this to true to call the action class constructor sending the action parameters to it
- default: false
- example
ExtDirect::$constructor_send_params = true;- name: debug
- type: boolean
- meaning: Set this to true to allow exception detailed information in the output
- default: false
- example
ExtDirect::$debug = true;- name: utf8_encode
- type: boolean
- meaning: Set this to true to pass all action method call results through utf8_encode function
- default: false
- example
ExtDirect::$utf8_encode = true;- name: default_api_output
- type: string
- meaning: API output format - available options are "json" (good for Ext Designer) and "javascript"
- default: "json"
- comments: Another way to enforce "javascript" output is to append the "?javascript" query string in the end of your PHP script URL; do this in the HTML <script> tag that refers to your API
example
ExtDirect::$default_api_output = "javascript";

Does current (ExtJS 4+), formal documentation of these directives exist? If so, might anyone be able to provide a URL? I have looked and came-up empty.

Thanks in advance.

bizsysdev3
25 Jun 2013, 1:25 PM
For those who use Architect, keep in mind that you cannot add a JS resource with url="ext-direct.php?javascript" because it will create a file with such name (including the query part after the question mark).


What you have to do instead is create a JS resource with url="ext-direct.php" and then in the first step (PHP code) override the $_GET array:


<?php


$_GET['javascript'] = 1; // hardcode the type


require_once 'ExtDirect.php';


class Server


...


Hope this helps someone.

j.bruni
2 Jul 2013, 5:47 AM
For those who use Architect, keep in mind that you cannot add a JS resource with url="ext-direct.php?javascript" because it will create a file with such name (including the query part after the question mark).

The whole "?javascript" query string stuff was implemented to satisfy demands of early Ext Designer version. It needed JSON output and didn't play well with query string. So, I've made the default (i.e., without query string) output to be JSON, so it could be used with Ext Designer.

It was 3 years ago. There was no "Sencha" and no "Architect" at that time...

Now, we have Sencha Architect. But I have not used/tried it yet.

So, by now, I have to ask: instead of hard-coding a change in the $_GET array, just removing the "?javascript" and using the JSON output does not work for Sencha Architect?

Thank you.

bizsysdev3
2 Jul 2013, 6:23 AM
instead of hard-coding a change in the $_GET array, just removing the "?javascript" and using the JSON output does not work for Sencha Architect?

Didn't work for me. Looks like it expects specifically JS.


Alternatively you can change the default var in ExtDirect.php on line 80:



static public $default_api_output = 'javascript';

stewardsencha
2 Jul 2013, 8:16 AM
I made separate entry points, one for SA, one for extJS (and a third for development).

In other words, take the option out of the parameters entirely.

Set it internally using different scripts that all end up at the same place.

Fast. Easy. Low Tech.

j.bruni
2 Jul 2013, 11:13 AM
Alternatively you can change the default var in ExtDirect.php on line 80:


static public $default_api_output = 'javascript';



Take the option out of the parameters entirely.

Set it internally using different scripts that all end up at the same place.

Nice! IMO, both these alternatives are better than a hardcoded change to the query string. In fact, setting it internally, without modifying the class, is as easy as:


ExtDirect::$default_api_output = 'javascript';

blomasky
31 Jul 2013, 6:59 PM
1st, I am still very new to ExtJs so maybe this is a simple problem, but when I have an error in my PHP code (like a misnamed parameter), there is nothing sent back to my application and I see a msg starting with: Cannot modify header information.... instead of a JSON object being returned.

help
bruce

j.bruni
1 Aug 2013, 5:53 AM
when I have an error in my PHP code (like a misnamed parameter), there is nothing sent back to my application and I see a msg starting with: Cannot modify header information.... instead of a JSON object being returned

You need to fix the server-side error.

Please, check where your PHP errors log file is located, and read the full error message there.

Hacker-CB
8 Dec 2013, 11:41 AM
Now this solution does not have support for namespace classes, like this:


ExtDirect::provide(array(
'MyNamepace\Class'
));

Below is the patch to add support of the php namespace:


if ( in_array( $class . '::' . $method->getName(), self::$form_handlers ) || ( strpos( $method->getDocComment(), '@formHandler' ) !== false ) )
$api_method['formHandler'] = true;

$methods[] = $api_method;
}

$class=str_replace('\\','.',$class);

$actions[$class] = $methods;
}





protected function call_action()
{
$class = $this->action;

$class=str_replace('.','\\',$class);

mynsa04
23 Jan 2014, 4:32 AM
It not work with SA 3.

When add Direct Resource in SA, all works fine, but after save and open in browser, i have error: "
TypeError: Ext.php is undefined
read: Ext.php.ProductsController.read"



{"url":".\/direct.php","type":"remoting","namespace":"Ext.php","descriptor":"Ext.php.REMOTING_API","actions":{"ProductsController":[{"name":"read","len":1}]}}


But works if i add direct url as JS Resource.

francois,gelinas5
28 Mar 2014, 4:52 PM
Hi I am new with Sencha Touch

I am doing a Sencha Touch application that use a form to search something in a mysql data base (with php). I tried the example (that I downloaded on this forum) of this script and had it work with my data base and everything works perfectly! But, when I try to apply the code in Sencha Touch (using the MVC pattern) It does not work. Even when I try to only do the imports in my main page (html file), my program don't work anymore :(

Can anyone help with that?
Thank you

Max_nl
18 Apr 2014, 7:29 AM
Vulnerability

This backend -like probably all other Ext.Direct implementations- is vulnerable to Cross-Site Request Forgery attacks.
If an attacker is able to trick a user that is currently logged into an Ext.JS webapplication to visit the attacker's website, it allows the attacker to execute arbritary Ext.Direct methods.

E.g. the attacker's website could include code like this:


<form id="attackform" method="post" action="http://your-server/your-extdirect-script.php">
<input type="hidden" name="extTID" value="1">
<input type="hidden" name="extType" value="rpc">
<input type="hidden" name="extAction" value="classname">
<input type="hidden" name="extMethod" value="somePrivilegedMethod">
<input type="hidden" name="p1" value="someParameter">
<input type="submit">
</form>
<script>document.getElementById("attackform").submit()</script>


Causing somePrivilegedMethod(someParameter) to be executed.
In a typical webapplication using PHP sessions, this will occur under the privileges of the authenticated user as the browser will send the session cookie it has for your-server with the request.

Patch

This patch attempts to prevent the problem by using the double submit cookies pattern (https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet#Double_Submit_Cookies)
An unique cookie is set when the browser fetches the API with <script src="your-extdirect-script.php?javascript"></script> and the backend expects the value of that cookie to be transmitted as request variable with every request.



protected function call_action()
{
$class = $this->action;

// Accept only calls to classes defined at "api_classes" configuration
if ( !in_array( $class, ExtDirect::$api_classes ) )
throw new Exception( 'Call to undefined or not allowed class ' . $class, E_USER_ERROR );

// Do not allow calls to magic methods; only allow calls to methods returned by "get_class_methods" function
if ( ( substr( $this->method, 0, 2 ) == '__' ) || !in_array( $this->method, get_class_methods( $class ) ) )
throw new Exception( 'Call to undefined or not allowed method ' . $class . '::' . $this->method, E_USER_ERROR );

// Do not allow calls to methods that do not pass the declare_method_function (if configured)
if ( !empty( self::$declare_method_function ) && !call_user_func( self::$declare_method_function, $class, $this->method ) )
throw new Exception( 'Call to undefined or not allowed method ' . $class . '::' . $this->method, E_USER_ERROR );

// Verify double submit cookie to prevent CSRF attacks
$token = '';
if ( isset( $_GET['extToken'] ) )
$token = $_GET['extToken'];
else if ( isset( $_POST['extToken'] ) )
$token = $_POST['extToken'];
if ( empty( $_COOKIE['extToken'] ) || $_COOKIE['extToken'] != $token )
throw new Exception( 'Double submit cookie value incorrect', E_USER_ERROR );


$ref_method = new ReflectionMethod( $class, $this->method );




static public function get_api_javascript()
{
$template = <<<JAVASCRIPT

if ( Ext.syncRequire )
Ext.syncRequire( 'Ext.direct.Manager' );

Ext.namespace( '[%namespace%]' );
[%descriptor%] = [%actions%];
Ext.Direct.addProvider( [%descriptor%] );
Ext.Ajax.extraParams = { extToken: Ext.util.Cookies.get('extToken') };

JAVASCRIPT;

/* Set double submit cookie for CSRF protection */
if ( empty( $_COOKIE['extToken'] ) )
{
if ( function_exists( 'openssl_random_pseudo_bytes' ) )
$rand = bin2hex( openssl_random_pseudo_bytes( 16 ) );
else
$rand = uniqid();

setcookie( 'extToken', $rand );
}

$elements = array(
'[%actions%]' => self::get_api_json(),
'[%namespace%]' => ExtDirect::$namespace,
'[%descriptor%]' => ExtDirect::$descriptor
);

return strtr( $template, $elements );
}

salarmehr
11 May 2014, 7:06 AM
Dear J. Bruni
I would like to appreciate for your wonderful Ext Direct Implementation.
I suggest putting the code on github. So we can contribute, report issues, adding documentations and examples and start the project.

dlbaz
4 Aug 2014, 1:14 PM
Hi guys,
Just trying to get this working in Sencha Touch 2 with no success.

I added the .js librarys to the .js section in app.json


{ "path":"http://extjs.cachefly.net/ext-3.2.1/adapter/ext/ext-base.js",
"remote": true
},
{
"path":"http://extjs.cachefly.net/ext-3.2.1/ext-all.js",
"remote": true
},
{
"path":"ExtDirect/example.php?javascript"
//Should this be remote too?
},

and tried to access the php object in Ext with no luck.



console.log( Ext.php );
//prints out undefined


Where is this php obejct created?