Amfphp 1.9 beta 2 is now available for download. This is a HUGE release, making amfphp the fastest Remoting implementation ever (I kid you not).
Remoting at the speed of C
As I've mentioned earlier, there are many potential bottlenecks in a Remoting implementation which can affect speed. AMF encoding and decoding at the PHP level is not optimal, since PHP is a dynamic language, and encoding and decoding things is a repetitive, CPU and memory intensive operation. PHP, at its heart, is a thin scripting language which is powered and extended by C.
Emanuele Ruffaldi, who had previously created a Remoting implementation for C++, created a C extension for PHP which does AMF encoding and decoding. We've tested it through and through, it is very stable, and let me tell you, it is ridiculously fast. On sample data varying in size and complexity, it's 50 to 200 times faster on pure encoding and decoding than the previous PHP solution. Of course, as I've explained earlier, encoding and decoding AMF is only one of the many things that amfphp does, and the bottleneck may or may not be there depending on the size of the data. This extension will really help when sending tons of complicated data over the wire, data like large recordsets and deeply nested object trees. As users create more and more complicated apps with Flex, this will ensure that amfphp won't fall behind, and that you can continue to concentrate on your UI instead of encoding/decoding matters.
I want to stress that you don't need the extension installed to run the new version of amfphp. The extension is an add-on to PHP that the new version of amfphp will detect, load and use if it finds it, and revert to the original PHP-based encoding/decoding if it doesn't.
Here are some real world, before and after tests of amfphp's speed with this new extension:
| test |
Hello world |
Big RecordSet (4x1800) |
Huge Recordset (20x2500) |
Mike 100 |
Mike 1000 |
Mike 10000 |
Mike 100,000 |
| total php |
28 |
140 |
1378 |
45 |
188 |
2228 |
timeout |
| total native |
21 |
55 |
165 |
20 |
26 |
99 |
988 |
| diff (%) |
25% |
61% |
88% |
56% |
86% |
96% |
|
| encode php |
1 |
89 |
1303 |
18 |
157 |
2164 |
timeout |
| encode native |
0 |
13 |
102 |
0 |
3 |
35 |
558 |
| diff (%) |
100% |
85% |
92% |
100% |
98% |
98% |
|
| size |
482 bytes |
78.4 KB |
961.5 KB |
7.2 KB |
69.6 KB |
720 KB |
7.3 MBs |
The tests are:
- A simple Hello world test
- Sending a recordset with all the cities in Quebec with their coordinates (1 integer column, 1 string column, 2 float columns)
- Sending a recordset with 2500 user profiles in a dating site (10 string columns and 10 int columns)
- Mike Potter's test, adjusted so it doesn't use AMF3 repeated strings, with 100, 1000, 10,000 and 100,000 elements.
The top half of the table shows the pingback time for every test, over localhost, with the top row showing the speed of amfphp without the extension and the bottom one with the extension. The bottom half of the table shows only the amount of time spent in the encoder.
At the extreme end of the table, you should notice that it takes amfphp with the C extension less than a second to send 7.3 MBs of data consisting of 100,000 array elements each containing an object with three fields. That is absolutely insanely fast, my friends. It's so fast in fact, that it takes Flash more time to decode the AMF data than it does for amfphp to create it, and it's such a ludicrously large amount of data that Flash 9 will time out when using ObjectUtil.toString on that dataset. Note also that the regular amfphp doesn't fare bad at all in these tests, as in the large recordset example.
I should note that the serializer times shown above include all the time spent in the serializer, not just the time calling amf_encode or writeData. Therefore in the case of the large recordset, the speed improvement is not as dramatic as in the other cases because most of the time is actually being spent in the mysql recordset adapter when using the C extension, which obscures the real speed improvement. Furthermore, these tests don't use deeply nested data, but with the limited testing I've done, I can assert that the speed improvements are even better when serializing large trees.
So here's how to enable this extension: download it from Emanuele's site. On Windows, place the dll in php's ext directory and add a php_extension=php_amf.dll line in php.ini; restart Apache. On *NIX, compile php --with-amf from the source. Once that's done, load gateway.php in a browser, and you should see "AMF C Extension is loaded and enabled" on the second line. The new extension should also appear in phpinfo(). You don't have to change a setting in amfphp to enable it, if it sees it, it'll use it, if it doesn't, it'll revert to the default PHP encoding/decoding.
Also note that on Emanuele's site there is documentation on the callbacks that the extension defines, and that Evert Pot is interested in adding support for it in SabreAMF. I've also notified Mark Piller at MidnightCoders, so they may support the extension in WebORB for php. Once the extension reaches 1.0, I would like to have a list of shared hosting services that support it on the amfphp site; if you're interested in supporting it give me a buzz.
Remoting at the speed of gzip
The second beta comes with another nice speed boost in the form of decreased transfer times over the wire, thanks to gzip encoding. Basically, if a browser sends an Accept:gzip header to the server (ie, all modern browsers since IE5), amfphp will detect it and send the content gzipped. For large amounts of data, this can result in much decreased network traffic; typical compression ratios can vary from 2:1 to 10:1, depending on data set.
The nice part is that, once again, you don't need to configure anything to get this to work; if amfphp finds the gzip extension on the server, and the browser accepts gzip encoding, it'll do it, if not, it'll revert to the default behavior. By default, only results which are more than 25KBs in length are gzipped, as below that I don't think it's worth it to compress the data; you can change this setting in gateway.php though.
Better recordset support
I've added better recordset support to amfphp, which now includes:
- MySQL
- SQLite
- PDO
- Zend::DB, including ZendDbTable_Rowset
- PHP Doctrine
- Postgres (untested)
- PEAR::DB (untested)
- ODBC (untested)
- MySQLi (untested)
- MS-SQL (untested)
- Informix (untested)
- Frontbase (untested)
- ADODB (untested)
- Oracle - oci8 (untested)
The untested adapters should work, since they used to in amfphp 1.2, but I can't install every database known to man so if you get errors, load up Charles or ServiceCapture, see the error that's being sent, and send me a buzz.
Also noteworthy is a new plain RecordSet class. Basically, sometimes you want to loop through your dataset before sending it to Flash; in this case you can use the RecordSet wrapper. For example:
for(var i =
0; i <
100; i++
)
{
$rows[] =
array("val" => i,
"cube" => i*i*i
);
}
return new RecordSet
($rows);
You will receive this as a RecordSet in AMF0 and as an ArrayCollection in AMF3.
Enhanced service browser
I've tightened up the service browser interface. Most noteworthy are the enhanced info tab, which shows profiling information for every call, and the tree view tab, which allows you to browse returned object in a tree control, à la ServiceCapture and Charles.
This service browser will serve as the basis for the new Universal Remoting service browser, which is an initiative by myself, Evert Pot (SabreAMF), Aaron Smith (AMFRUBY) and Zoltan Csibi (Fluorine) to create a service browser that will work across all these Remoting implementations. That's right, all the warm fuzzy feeling of amfphp's service browser will be available in [your favorite Remoting implementation]. Fuggawesome.
ByteArray support
ByteArray support was in the last version, but I just didn't document it. Let me do that right now:
If you send a ByteArray instance from Flex to amfphp, you will receive an instance of ByteArray (a custom PHP class) in your method. The data is in $byteArray->data. To send back a ByteArray, just return an instance of ByteArray. For example, to save a bitmap through amfphp, in ActionScript:
var ba:ByteArray = bmp.getBytes();
ba.compress();
service.saveBitmap(ba);
In PHP:
function saveBitmap
($ba)
{
$data =
$ba->
data;
$data =
gzuncompress($data);
file_put_contents
("rawdata.rgba",
$data);
return true;
}
And to send a ByteArray to Flash:
Removed things
I've removed webservice support. If you didn't know, basically the original Remoting spec allowed you to call a SOAP-based webservice through a Remoting gateway. This was thought up in the days of Flash 6, when there wasn't an easy way to consume SOAP web services in Flash. Since Flash 7, there is a component that can connect to SOAP web services, and of course in Flex 2, it's as simple as using mx:WebService. Thus the only advantage of consuming a SOAP service through amfphp is the "free proxy", which allows you to bypass crossdomain.xml. However, it added bloat to amfphp, it was buggy, and SOAP support in PHP is pretty sucky anyway, which meant you were better off calling the webservice directly. Thus is was removed.
I've also removed a lot of settings in gateway.php which were seldom used and confused users. globals.php now contains global settings such as the service path, and this is the recommended place to add things like libraries or global includes (ie, for frameworks that expect to be included as globals, like WordPress, TextPattern, etc.).
Bug fixes
A few bugs with regards to encoding/decoding were fixed (as noticed by comparing with the C extension). A few random compatibility problems with low permissions on shared hosting were fixed. Dead code was removed. json.php can now accept an ? instead of / as the first character after it for incompatible web servers. Lots of other little things I didn't write down and can't remember right now were patched.
What's next
JSON-RPC, as opposed to straight JSON, enhanced service browser will be in the 2.0 release. I will try to find a more comprehensive solution to authentication, but I'm starting to run out of ideas. Pageable recordsets will definitely not make it back into 2.0. Zoltan figured out how to get FDS functionality working in Fluorine and we may implement a subset of that, but not until after 2.0 (and with that pageable recordsets will be back, but also lots of other nice things).