NotionCommotion Posted August 24, 2016 Share Posted August 24, 2016 How do I simulating a POST request? I do not wish to use cURL, but wish to execute the script directly in an IDE. It needs to be more than just setting the $_POST super variable, and needs to actually set the HTTP body header. Below is my unsuccessful attempt. Thank you PS. Why doesn't var_dump(file_get_contents('php://input')); display anything? <?php ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); function simulateRequest($method,$url,$get,$post) { $query=http_build_query($get); $_GET=$get; $_POST=$post; $_REQUEST=array_merge($_GET,$_POST,$_REQUEST); $_SERVER['REQUEST_METHOD']=$method; $_SERVER['REDIRECT_URL']=$url; $_SERVER['QUERY_STRING']=$query; $_SERVER['REQUEST_URI']=$url.($query?'?'.$query:null); if($query) {$_SERVER['REDIRECT_QUERY_STRING']=$query;} if(!empty($post)) { //HttpRequest::setBody (http_build_query($post)); //http://php.net/manual/it/httprequest.setbody.php $opts = ['http' =>['method' => 'POST','header' => 'Content-type: application/x-www-form-urlencoded','content' => http_build_query($post)]]; $context = stream_context_create($opts); $rawInput = fopen('php://input', 'r'); stream_copy_to_stream($context,$rawInput); } } simulateRequest('POST','/test/bla',[],['a'=>'A','b'=>'B','c'=>'C']); var_dump(file_get_contents('php://input')); print_r($_POST); use \Psr\Http\Message\ServerRequestInterface as Request; use \Psr\Http\Message\ResponseInterface as Response; require '../vendor/autoload.php'; $app = new \Slim\App(); $app->post('/test/{attr}', function (Request $request, Response $response) { echo('Attr:<pre>'.print_r($request->getAttribute('attr'),1).'</pre>'); exit('Params<pre>'.print_r($request->getParsedBody(),1).'</pre>'); }); $app->get('/test/{attr}', function (Request $request, Response $response) { echo('Attr:<pre>'.print_r($request->getAttribute('attr'),1).'</pre>'); exit('Params<pre>'.print_r($request->getQueryParams(),1).'</pre>'); }); $app->run(); Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted August 24, 2016 Share Posted August 24, 2016 You're trying to copy a stream context to the standard input. What is this supposed to do? I tend to use Burp Suite for manual testing. It allows you to intercept actual requests, edit them, repeat them, create new requests etc. For automated tests, cURL actually is the right tool. Trying to create a “fake” request doesn't really make sense to me. You have to go through plenty of parameters, and then you may still miss something, which means your test won't do what an actual request does. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted August 24, 2016 Author Share Posted August 24, 2016 Yea, that copy of a string thing didn't make any sense. How can Burp Suite allow one to debug the script line by line? Agree with using cURL for automated testing. Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted August 24, 2016 Share Posted August 24, 2016 Which debugger are you using? XDebug? With remote debugging, you can make the IDE listen for debug connections which are triggered by HTTP requests. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted August 24, 2016 Author Share Posted August 24, 2016 Which debugger are you using? XDebug? http://www.nusphere.com/products/phped.htm Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted August 25, 2016 Share Posted August 25, 2016 Never used this, so you're on your own. Quote Link to comment Share on other sites More sharing options...
requinix Posted August 25, 2016 Share Posted August 25, 2016 (edited) Looks like PhpEd has its own debugger they want you to use? If that doesn't work for you then you can look to see if there's a way to get Xdebug working with it instead. (Netbeans user here so I don't know.) As for php://input, that will be empty unless you're running the script through the console with something piped into it. (I think - that might only be for stdin.) I don't think you can give it a value short of implementing your own php:// wrapper, which would suck. I think the best option would be two scripts: one script calls the other through the shell and pipes in the $post data, then the other to fill in $_GET and $_POST and such appropriately. I've used that kind of strategy before with my own code... which is somewhere... I don't quite remember... It's used as part of a unit testing framework, albeit mostly for launching PHP processes under specific configurations. // first script function simulateRequest($method, $url, $get, $post) { $query = http_build_query($get ?: []); $input = http_build_query($post ?: []); // pass most information through argv $args = implode(" ", array( escapeshellarg($path_to_second_script), escapeshellarg($method), escapeshellarg($url), escapeshellarg($query) )); $descriptors = array( 0 => array("pipe", "r"), 1 => STDOUT, 2 => STDERR ); // launch php with the second script and write to it the post data $p = proc_open(PHP_BINARY . " -- " . escapeshellarg($path_to_second_script) . " " . , $descriptors, $pipes); fwrite($pipes[0], $input); fclose($pipes[0]); proc_close($p); } list(, $method, $url, $query) = $argv; parse_str($query ?: "", $_GET); parse_str(file_get_contents("php://input"), $_POST); $_REQUEST = // really you should evaluate this according to request_order // etc. // then continue the startup process Meanwhile $opts = ['http' =>['method' => 'POST','header' => 'Content-type: application/x-www-form-urlencoded','content' => http_build_query($post)]]; $context = stream_context_create($opts); $rawInput = fopen('php://input', 'r'); stream_copy_to_stream($context,$rawInput); is really, really wrong. OH FOR FRICK'S SAKE, why is the indentation broken? Blargh. Edited August 25, 2016 by requinix Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted August 25, 2016 Author Share Posted August 25, 2016 Thanks Requinx, Yea, I know my streaming thing was REALLY wrong, but was just grasping at straws. Your approach might work but that dang indention must have deleted half of it, and I will probably go with something like the following today. I still would like a better solution. <?php class request { public $method, $attr=[], $get=[], $post=[]; public function __construct($method,$request,$attr) { $this->method=$method; if($method=='GET'){$this->get=$request;} else {$this->post=$request;} $this->attr=$attr; } public function getMethod() { return $this->method; } public function getParsedBody() { return $this->post; } public function getQueryParams() { return $this->get; } public function getAttribute($name) { return $this->attr[$name]; } } class slim { public function __construct($config) { $db = $config['db_sql']; $this->db=new \PDO("mysql:host={$db['host']};dbname={$db['dbname']};charset={$db['charset']}",$db['username'],$db['password'],array(\PDO::ATTR_EMULATE_PREPARES=>false,\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>true,\PDO::ATTR_ERRMODE=>\PDO::ERRMODE_EXCEPTION,\PDO::ATTR_DEFAULT_FETCH_MODE=>\PDO::FETCH_ASSOC)); $account=new \myApp\Accounts($this->db); $this->account=$account->get('main_key'); $this->logger=null; } } ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); spl_autoload_register(function ($classname) { //Make better $parts = explode('\\', $classname); require "../classes/".end($parts).".php"; }); $config=parse_ini_file('../../config.ini',true); $config=[ 'db_sql'=>$config['mysql'], 'displayErrorDetails'=> true, 'determineRouteBeforeAppMiddleware' => true, ]; $slim=new slim($config); $request=new request('POST',['x'=>1],['y'=>2]); //Request Method, GET or POST array, atributes array $factory=new Factory($request,$slim); $rs=$factory->testSomething(/*...*/); echo('<pre>'.print_r($rs,1).'</pre>'); Quote Link to comment Share on other sites More sharing options...
requinix Posted August 25, 2016 Share Posted August 25, 2016 That's an option? That class? You should do that. Yeah it's more code, but it's more flexible and lends itself really well to testing. Quote Link to comment Share on other sites More sharing options...
Barand Posted August 25, 2016 Share Posted August 25, 2016 http://www.nusphere.com/products/phped.htm PHPEd allows you to specify POST values Select "Run/Parameters" from the menu and enter the parameter names and values with type=post. Run the script using either the embedded IE or embedded Mozilla browsers (doesn't work with with embedded Chrome for some reason) 1 Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted August 25, 2016 Author Share Posted August 25, 2016 That's an option? That class? You should do that. Yeah it's more code, but it's more flexible and lends itself really well to testing. Sarcasm? It is not ideal as it just allows me to test the methods. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted August 25, 2016 Author Share Posted August 25, 2016 PHPEd allows you to specify POST values Select "Run/Parameters" from the menu and enter the parameter names and values with type=post. Run the script using either the embedded IE or embedded Mozilla browsers (doesn't work with with embedded Chrome for some reason) Perfect! Thank you! PHPed doesn't seem real good about documenting some features, or maybe I just can't find them. Do you know if there is something similar which will change the URI from say index.php to index.php/foo? Quote Link to comment Share on other sites More sharing options...
Barand Posted August 25, 2016 Share Posted August 25, 2016 Sorry, I don't. You could try contacting the Support team at Nusphere. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted August 25, 2016 Author Share Posted August 25, 2016 Sorry, I don't. You could try contacting the Support team at Nusphere. Well, you got me past the hard part. I could always just change $_SERVER to change the URI for testing. Thanks again. Quote Link to comment Share on other sites More sharing options...
Barand Posted August 25, 2016 Share Posted August 25, 2016 Not a facility I have ever used but is this what you are looking for? Settings/Run & Debug/SRV Web Server/Server mappings Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted August 27, 2016 Author Share Posted August 27, 2016 Hey Requinix, I have been trying to implement your solution, but am stuck. What should I witness when proc_open() is executed? Note that the second file never seems to execute. Thanks private function simulateRequest($method, $url, $data, $path_to_second_script, $keys) { syslog(LOG_INFO,"$method | $url | ".json_encode($data)); foreach($keys as $key=>$value){ $_SERVER["HTTP_X_$key"]=$value; } $query = $method=="GET"?http_build_query($data):''; $input = $method!="GET"?http_build_query($data):''; // pass most information through argv $args = implode(" ", array( //escapeshellarg($path_to_second_script), escapeshellarg($method), escapeshellarg($url), escapeshellarg($query) )); $descriptors = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("file", "/tmp/error-output.txt", "a") ); // launch php with the second script and write to it the post data $p = proc_open(PHP_BINARY . " -- " . escapeshellarg($path_to_second_script) . " " .$args , $descriptors, $pipes); fwrite($pipes[0], $input); fclose($pipes[0]); proc_close($p); } <?php // second script syslog(LOG_INFO,"Got here!"); list(, $method, $url, $query) = $argv; parse_str($query ?: "", $_GET); parse_str(file_get_contents("php://input"), $_POST); $_REQUEST = 123;// really you should evaluate this according to request_order // etc. echo('xxx'); //require('index.php'); Quote Link to comment Share on other sites More sharing options...
requinix Posted August 27, 2016 Share Posted August 27, 2016 Output PHP_BINARY . " -- " . escapeshellarg($path_to_second_script) . " " .$argsand make sure it's the correct command. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted August 28, 2016 Author Share Posted August 28, 2016 Still nothing. One thing I found was that PHP_BINARY is set to an empty string. Any ideas why? Regardless, I hard coded the location of PHP, and tried it again, but still no. file1.php writes to the syslog, but not file2.php. What might be the problem? Thank you <?php openlog("myScriptLog", LOG_PID | LOG_PERROR, LOG_LOCAL1); syslog(LOG_INFO,"File1"); $script=__DIR__.'/file2.php'; $url='hello'; $method='GET'; $data=['a'=>1,'b'=>2, 'c'=>3,'d'=>4]; $query = $method=="GET"?http_build_query($data):''; $input = $method!="GET"?http_build_query($data):''; $args = implode(" ", array( escapeshellarg($method), escapeshellarg($url), escapeshellarg($query) )); $descriptors = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("file", "/tmp/error-output.txt", "a") ); // Why is PHP_BINARY set to an empty string? var_dump(PHP_BINARY); $command="/usr/bin/php -- " . escapeshellarg($script) . " " .$args; var_dump($command); $p = proc_open($command, $descriptors, $pipes); var_dump($p); var_dump($pipes); fwrite($pipes[0], $input); fclose($pipes[0]); proc_close($p); echo('File 1'); <?php openlog("myScriptLog", LOG_PID | LOG_PERROR, LOG_LOCAL1); syslog(LOG_INFO,"File2"); echo('file2'); Output of file1.php string(0) "" string(95) "/usr/bin/php -- '/var/www/datalogger/src/public/test/file2.php' 'GET' 'hello' 'a=1&b=2&c=3&d=4'" resource(4) of type (process) array(2) { [0]=> resource(2) of type (stream) [1]=> resource(3) of type (stream) } File 1 Quote Link to comment Share on other sites More sharing options...
requinix Posted August 28, 2016 Share Posted August 28, 2016 Bwahaha, I got the command wrong. args... Arguments passed to script. Use -- args when first argument starts with - or script is read from stdinSo I was a bit overzealous in using --. The command should be $command="/usr/bin/php" . escapeshellarg($script) . " " .$args; Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted August 28, 2016 Author Share Posted August 28, 2016 Ah, much better! By the way, what does the -- do? Granted, you didn't mean to include it, but I suppose it is typically use to do something, and it is one of those things that are impossible to google. Also, any idea why I don't have a value for PHP_BINARY? Thanks! Quote Link to comment Share on other sites More sharing options...
requinix Posted August 28, 2016 Share Posted August 28, 2016 -- universally(ish) means to stop processing command line arguments and treat the rest as literals. If you did php -f file.php -hthen PHP would think you wanted to see its help. If you did php -f file.php -- -hthen PHP would run file.php and pass it "-h" in $argv[1]. If you used -f with the command then you would need --, but without -f PHP implicitly stops parsing arguments after the filename. php filename args... php -f filename -- args... And no, I don't know why PHP_BINARY doesn't have a value. Are you calling the first script from the command line? Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted August 29, 2016 Author Share Posted August 29, 2016 I think I once knew about "--", but failed to remember. I really need to better commit it to memory! No, not called from the command line. Since tried it, and same results. My PHP install was off some YUM repository, and is probably the culprit. Off topic, but do you recommend compiling from source? While I am glad I learned, I don't think this solution will address my immediate needs. I wanted the debugger to magically jump to the second script and allow me to go line-by-line or skip to breakpoints, but I believe it will appear the same as a cURL required (i.e. here is your stuff). Thank you for your help! Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted August 29, 2016 Share Posted August 29, 2016 I wanted the debugger to magically jump to the second script and allow me to go line-by-line or skip to breakpoints If you can't get this to work in your IDE, consider switching to XDebug and a mainstream IDE. PhpStorm is great (but expensive), Netbeans is fine. Quote Link to comment Share on other sites More sharing options...
requinix Posted August 29, 2016 Share Posted August 29, 2016 Yeah, the debugger works on one single process - when you start the second script it won't follow along. If you had Xdebug then you could launch that second PHP with the right options to automatically start debugging, implying that you were not debugging the first script, but now it's starting to get complicated. Are you sure you need php://input to work? Would you be able to hack it such that it uses a different path, like "php-dynamic://input", then register your own php-dynamic stream that wraps php normally and your testing library stuff when... testing? That sounds like something I would look into myself. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.