i've just spent the pass 2 days trying to get AGI to work with PHP; i made a lot of silly mistakes along the way which could have been avoided if only there were some kinda howto or samples. at the risk of looking stupid, i decided to shared my experience in hopes that it might help some newbie get going with PHP. 1. first order of business is to be aware of your php environment; i m NOT saying you should change anything but you should be aware of it as it may spare you some frustration along the way especially when you are in the thick of things. look in your php config file (usually in /etc/php.ini) for the following: ob_implicit_flush(false); set_time_limit(5); ; error_log = filename the first item shows whether php should buffer output; in the case of * agi, if you buffer your output, * will not get your instruction for a long time unless you flush the buffer manually (see below) the second item is the max. allowable time to run your php script. most * agi script will run within a reasonable time but if you have a very lengthy script that is producing strange errors, it is possible that your script was terminated prematurely. the third item is Log; great for debug but a killer for production system. it might have been turned off by default or you may have turn it off on purpose and don't remember. 2. put your scripts in the following directory and get it working first before you do anything fancy /var/lib/asterisk/agi-bin/ 3. remember to chmod ALL your script to 755 as follows: chmod 755 *.php 4. the very first 2 lines in your script should be as follows: (assuming that your php bindery is in /usr/bin; double check now) #!/usr/bin/php -q <?php notice: there are no gaps and no white space char (except a single \n) between lines 1 and 2. otherwise strange things will be sent to stdout and ruin your day! 5. next, you should use fopen() to create all your needed handles. i know different versions of php have varying features to deal with stdio streams but fopen() will work with most new and old versions making your scripts more portable and you don't have to fidget with the php.ini file. besides, fopen() does not pose any inconvenience for use with * agi, so use it. $stdin = fopen('php://stdin', 'r'); $stdout = fopen('php://stdout', 'w'); $stdlog = fopen('my_agi.log', 'w'); 6. * always sends a bunch of info each time agi is called as follows: agi_request: test.php agi_channel: Zap/1-1 agi_language: en agi_type: Zap agi_callerid: agi_dnid: agi_context: default agi_extension: 1000 agi_priority: 1 . . if you need the info, save it; otherwise throw them away as follows: while(($data = fgets($stdin,1024)) != "\n") { // codes to save the info goes here // or do nothing } 7. this is the point where you can start talking with *. use fputs to send * agi commands; some people use echo but i prefer fputs: fputs($stdout,"SAY NUMBER 1234567 '79#' \n"); fflush($stdout); 7a. use fflush() regardless of php.ini setting just to be safe, does hurt if you don't fflush() (auto or manual), * will not receive the command and your app will be stuck there until timeout. 7b. use of quotes; * agi command options are not optional i.e. they must appear on the command string * some options MUST be enclosed in quotes e.g. <escape digits> of SAY NUMBER and SAY DIGITS * some options MUST NOT be enclosed in quotes e.g. <digit string> of SAY NUMBER and SAY DIGITS * some options can go either way. * you can use single quote where quote is needed e.g. as above * as a reminder, escape char such as \n in a strings within sigle quotes will not be resolved !!!! but i'm sure you already know that. 8. next, you'll have to pick up responses from * which is pretty straight forward: $msg = fgets($stdin,1024); fputs($stdlog,$msg . "\n"); 9. if your script failed for some reason (especially if you forget to fflush()), the process is actually hung in limbo; REMEMBER to killproc your script process before you test again: killproc my_script.php 10. the purpose of this is to show the PHP aspects of * agi and not agi in general; there are many excellent documentation on * agi such as the following, you should read that as well: http://home.cogeco.ca/~camstuff/agi.html 11. sample.php #!/usr/bin/php -q <?php $stdin = fopen('php://stdin', 'r'); $stdout = fopen('php://stdout', 'w'); $stdlog = fopen('my_agi.log', 'w'); while(($abc = fgets($stdin,1024)) != "\n") { fputs($stdlog,$abc); // or parse the data for use } fputs($stdout,"ANSWER\n"); fflush($stdout); $abc = fgets($stdin,1024); fputs($stdlog,$abc . "\n"); fputs($stdout,"GET DATA aa-welcome '1' 1\n"); fflush($stdout); $abc = fgets($stdin,1024); fputs($stdlog,$abc . "\n"); fputs($stdout,"EXEC Meetme 1234|p \n"); // <=- notice the |p in 1234|p being the option for meetme fflush($stdout); $abc = fgets($stdin,1024); fputs($stdlog,$abc . "\n"); exit(); ?>
Hi, At 13:48 11-11-2003 +0800, you wrote:>i made a lot of silly mistakes along the way which could have been >avoided if only there were some kinda howto or samples. at the risk >of looking stupid, i decided to shared my experience in hopes that >it might help some newbie get going with PHP.I've done this a long time ago, and by sheer accidence someone posed a question about it yesterday. Below are some of the comments I sent him. By the way, I also have a reusable AGI function library in PHP. I think I posted an older version to the list a long long time ago, but if there is interest I can clean it up once more and put it out there... Using normal function calls instead of the handywork saves the programmer from nasty little bugs :-)) ---*SNIP*--- First of all, never sleep() in AGI, if you need timeouts, do a WAIT FOR DIGIT or something, otherwise you might end up deadlocking your asterisk server. Secondly, after issueing a command, read back the results, that way you can be sure your command finished and you can even get some input about what happened. Asterisk posts back result codes too... Thirdly, don't forget newlines where they are needed. I've adjusted your script accordingly and added some functions to ensure some of these things: #!/usr/bin/php4 -q <?php ob_implicit_flush(true); set_time_limit(0); $in = fopen("php://stdin","r"); // toggle debugging output (more verbose) $debug = false; // Do function definitions before we start the main loop function read() { global $in, $debug; $input = str_replace("\n", "", fgets($in, 4096)); if ($debug) echo "VERBOSE \"read: $input\"\n"; return $input; } function errlog($line) { global $err; echo "VERBOSE \"$line\"\n"; } function write($line) { global $debug; if ($debug) echo "VERBOSE \"write: $input\"\n"; echo $line."\n"; } // parse agi headers into array while ($env=read()) { $s = split(": ",$env); $agi[str_replace("agi_","",$s[0])] = trim($s[1]); if (($env == "") || ($env == "\n")) { break; } } // main program errlog("Call from ".$argv[1]." - Calling phone"); read(); write("SAY DIGITS 22 X"); // X is the escape digit. since X is not DTMF, no exit is possible :-) read(); write("SAY NUMBER 2233 X"); // X is the escape digit. since X is not DTMF, no exit is possible :-) read(); // clean up file handlers etc. fclose($in); exit; ?> ---*SNIP*--- best regards, Florian
hkirrc.patrick wrote:> i've just spent the pass 2 days trying to get AGI to work with PHP; > > i made a lot of silly mistakes along the way which could have been > avoided if only there were some kinda howto or samples. at the risk > of looking stupid, i decided to shared my experience in hopes that > it might help some newbie get going with PHP.Thank you! I've edited your text slightly and published it on the wiki. http://www.voip-info.org/tiki-index.php?page=Asterisk+AGI+php Regards, /Olle
Thanks for that .. I have also just started writing a AGI in PHP and have also discovered some of the issues.. you have shown me a few more that I probably would have bumped into.. Should save me a little stress.. :) Later.. hkirrc.patrick wrote:> i've just spent the pass 2 days trying to get AGI to work with PHP; > > i made a lot of silly mistakes along the way which could have been > avoided if only there were some kinda howto or samples. at the risk > of looking stupid, i decided to shared my experience in hopes that > it might help some newbie get going with PHP. > > 1. first order of business is to be aware of your php environment; i m > NOT saying you should change anything but you should be aware > of it as it may spare you some frustration along the way especially > when you are in the thick of things. look in your php config file > (usually in /etc/php.ini) for the following: > > ob_implicit_flush(false); > set_time_limit(5); > ; error_log = filename > > the first item shows whether php should buffer output; in the case > of * agi, if you buffer your output, * will not get your instruction > for a > long time unless you flush the buffer manually (see below) > > the second item is the max. allowable time to run your php script. > most > * agi script will run within a reasonable time but if you have a very > lengthy script that is producing strange errors, it is possible that > your > script was terminated prematurely. > > the third item is Log; great for debug but a killer for production > system. > it might have been turned off by default or you may have turn it off > on purpose and don't remember. > > 2. put your scripts in the following directory and get it working first > before you do anything fancy > > /var/lib/asterisk/agi-bin/ > > 3. remember to chmod ALL your script to 755 as follows: > > chmod 755 *.php > > 4. the very first 2 lines in your script should be as follows: > (assuming that your php bindery is in /usr/bin; double check now) > > #!/usr/bin/php -q > <?php > > notice: there are no gaps and no white space char (except a single \n) > between lines 1 and 2. otherwise strange things will be sent to stdout > and ruin your day! > > 5. next, you should use fopen() to create all your needed handles. i > know > different versions of php have varying features to deal with stdio > streams > but fopen() will work with most new and old versions making your > scripts > more portable and you don't have to fidget with the php.ini file. > besides, > fopen() does not pose any inconvenience for use with * agi, so use it. > > $stdin = fopen('php://stdin', 'r'); > $stdout = fopen('php://stdout', 'w'); > $stdlog = fopen('my_agi.log', 'w'); > > 6. * always sends a bunch of info each time agi is called as follows: > > agi_request: test.php > agi_channel: Zap/1-1 > agi_language: en > agi_type: Zap > agi_callerid: > agi_dnid: > agi_context: default > agi_extension: 1000 > agi_priority: 1 > . > . > if you need the info, save it; otherwise throw them away as follows: > > while(($data = fgets($stdin,1024)) != "\n") > { > // codes to save the info goes here > // or do nothing > } > > 7. this is the point where you can start talking with *. use fputs to > send * agi commands; some people use echo but i prefer fputs: > > fputs($stdout,"SAY NUMBER 1234567 '79#' \n"); > fflush($stdout); > > 7a. use fflush() regardless of php.ini setting just to be safe, does hurt > if you don't fflush() (auto or manual), * will not receive the command > and your app will be stuck there until timeout. > > 7b. use of quotes; > * agi command options are not optional > i.e. they must appear on the command string > * some options MUST be enclosed in quotes > e.g. <escape digits> of SAY NUMBER and SAY DIGITS > * some options MUST NOT be enclosed in quotes > e.g. <digit string> of SAY NUMBER and SAY DIGITS > * some options can go either way. > * you can use single quote where quote is needed e.g. as above > * as a reminder, escape char such as \n in a strings within > sigle quotes > will not be resolved !!!! but i'm sure you already know that. > > 8. next, you'll have to pick up responses from * which is pretty > straight forward: > > $msg = fgets($stdin,1024); > fputs($stdlog,$msg . "\n"); > > 9. if your script failed for some reason (especially if you forget to > fflush()), the process > is actually hung in limbo; REMEMBER to killproc your script process > before you > test again: > > killproc my_script.php > > 10. the purpose of this is to show the PHP aspects of * agi and not > agi in general; > there are many excellent documentation on * agi such as the following, > you should read that as well: > > http://home.cogeco.ca/~camstuff/agi.html > > 11. sample.php > > #!/usr/bin/php -q > <?php > > $stdin = fopen('php://stdin', 'r'); > $stdout = fopen('php://stdout', 'w'); > $stdlog = fopen('my_agi.log', 'w'); > > while(($abc = fgets($stdin,1024)) != "\n") > { > fputs($stdlog,$abc); > // or parse the data for use > } > > fputs($stdout,"ANSWER\n"); > fflush($stdout); > $abc = fgets($stdin,1024); > fputs($stdlog,$abc . "\n"); > > fputs($stdout,"GET DATA aa-welcome '1' 1\n"); > fflush($stdout); > $abc = fgets($stdin,1024); > fputs($stdlog,$abc . "\n"); > > fputs($stdout,"EXEC Meetme 1234|p \n"); // <=- notice the |p in > 1234|p being the option for meetme > fflush($stdout); > $abc = fgets($stdin,1024); > fputs($stdlog,$abc . "\n"); > > exit(); > > ?> > > > > _______________________________________________ > Asterisk-Users mailing list > Asterisk-Users@lists.digium.com > http://lists.digium.com/mailman/listinfo/asterisk-users >