API add Location Header and 302 - php

I've written an API using a simple but fairly long query string request.
The requester can designate an XML or HTML response.
The query is received and the data is processed. The HTML simply echos back a response. In order to avoid redundant code, I use a JavaScript redirect to open and process the XML. That looks like this:
$QString = '';
foreach($XMLItems as $key => $value)
{
$QString .= $key.'^'.$value.'|';
}
$QString = substr($QString,0,-1);
Redirect('XMLProcess.php?Q='.$QString);
Redirect() is a simple function that runs javascript:
function Redirect($n)
{
die("<script type=\"text/javascript\">window.location='$n';</script>");
}
The XML construction looks like this:
<?php
$dom = new DOMDocument("1.0");
header ("Content-Type:text/xml");
$QArray = explode('|',$_REQUEST[Q]);
foreach($QArray as $value)
{
$x = explode('^',$value);
$XMLItems[$x[0]] = $x[1];
}
$root = $dom->createElement("Data");
$dom->appendChild($root);
foreach($XMLItems as $key => $value)
{
$key = $dom->createElement($key);
$root->appendChild($key);
$variable = $dom->createTextNode($value);
$key->appendChild($variable);
}
echo $dom->saveXML();
?>
I'm pretty ignorant as far as API's and what someone may be using on the receiving end. I have a client who is asking for a 302 redirect and location header. I am assuming my Redirect() function may be throwing his software off, but I don't really know. Of course, I cannot redirect to the XML file immediately, as the incoming data needs to be processed first. So I am trying to wrap my mind around what the client needs without duplicating the processing in a second file. And, since I am in the dark, the Redirect may not be the problem, anyway.

To emit a 302 Redirect (or indeed any status code), you need to use header. Per the docs:
The second special case is the "Location:" header. Not only does it send this header back to the browser, but it also returns a REDIRECT (302) status code to the browser unless the 201 or a 3xx status code has already been set.
which would be implemented like:
function Redirect($n)
{
header("Location: $n");
exit(0);
}
As noted in the comments, since your script is emitting output prior to the invocation of Redirect, you'll need to use ob_start (and friends) to capture that output and emit it after the header has been sent:
ob_start();
// ...
$QString = '';
foreach($XMLItems as $key => $value)
{
$QString .= $key.'^'.$value.'|';
}
$QString = substr($QString,0,-1);
Redirect('XMLProcess.php?Q='.$QString);
function Redirect($n)
{
header("Location: $n");
$contents = ob_get_clean();
// echo $contents; // if you want or need to
// you might also consider leaving this, but clients will
// honor the 302 before executing any Javascript
die("<script type=\"text/javascript\">window.location='$n';</script>");
}
All of this said, though, the code is feeling pretty janky. If at all possible, consider refactoring the HTML and XML versions so that the main work of the code is done in an included helper, and the HTML and XML "front-ends" just focus on rendering the output in the desired format.

Related

Redirect in PHP - Show txt file content

I'm just learning to code and I don't know my way around yet.
I already know how to redirect to another page with php.
for example you go to http://example.com/test.php/ then you are redirected to http://example.com/test.txt with the following code:
"header('Location: http://example.com/test.txt);"
But now I'm not sure how to show the content of different files on your domain if the requested url has
?data=1 or ?seid=2 after the php. For example:
"http://example.com/test.php?data=request1" shows the text from
"http://example.com/test1.txt"
"http://example.com/test.php?data=request2" shows the text from
"http://example.com/test2.txt"
Have informed me so far that I have found something with $DataArray but don't know exactly how to use it? I tried something with it:
<?php
$DataArray = array(
"request1" => "test1",
"request2" => "test2"
if(isset($_GET['data'])) {
$data = str_replace(" ", "+", $_GET["data"]);
if(array_key_exists($data, $DataArray))
echo trim(json_decode(file_get_contents('data/'. $DataArray[$data] .'.txt'),JSON_UNESCAPED_SLASHES), '"');
else
echo "badrequest";
}
?>
Sadly that doesn't work for me so I don't know how to manage that.
edit: was able to fix it thanks for your help.
result:
<?php
$targets = array("1" => "http://redirect-new.com/", "2" => "http://redirect-old.com/", /* ... */);
if (isset($_GET["data"]) && array_key_exists($_GET["data"], $targets)) {
header("Location: {$targets[$_GET["data"]]}");
exit;
}
?>
Pass the file name in the link without using the extension and try this code:
if(isset($_GET['data']))
{
$data = $_GET['data'];
$filename = $data.'.txt'; // here we will get the name of the file with the extension (.Text)
$FilePath = '/www/path/to/file'; // your directory files here
$Link = $FilePath.'/'.$filename;
/*
* Here we will find out whether the file exists or not :
*/
// if use include :
if(file_exists($Link)){
include $Link;
}else{
print 'No File Exist !';
}
// if use header location
if(file_exists($Link)){
header('Location: http://example.com/'.$Link);
}else{
print 'No File Exist !';
}
}
This will work with you, but you should read more about the HTTP protocol It will benefit you more in the future .
You need to learn a few things, let me try to help you complete a 'reading list' that will help you to understand what you're trying to do and how to do it.
First, you want to learn your way around HTTP protocol. It's a basic knowledge that will get you far - it's great to be aware of this protocol - it will help you identify what you need in different scenarios.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview
pay attention to http messages part.
Then, see how PHP abstracts different parts of HTTP protocol, here's the GET parameters:
https://www.php.net/manual/en/reserved.variables.get.php - something you will have to make use of.
Also you'll have to read files: https://www.php.net/manual/en/function.file-get-contents.php

PHP methods outputting HTML with incorrect structure?

I have a strange situation that I've never encountered where the returned HTML from my class methods doesn't output in the correct structure. For some unknown reason, everything is nested inside the HTML within the first loop, second loop, etc.
index.php
<body>
some html...
<div class="usb-port-container">
<?= $generate->output('short_name',id,'hostname'); ?>
</div>
some html...
<div class="usb-port-container">
<?= $generate->output('short_name',id,'hostname'); ?>
</div>
some html...
</body>
class.generate.php
function __construct() {
$this->getCopy = new getCopyData();
$this->getDrive = new getDriveData();
$this->helper = new helper();
}
function output ($short_name,$id,$hostname) {
$portCount = $this->getCopy->portCount($hostname, 'total');
for ($port = 0; $port < $portCount; ++$port) {
if ($this->getCopy->probePort($hostname,$port)) {
$default = $this->usbPortDefault($short_name,$id,$hostname,$port);
return $default;
} else {
$details = $this->usbPortDetails($short_name,$id,$hostname,$port);
return $details;
}
}
}
function usbPortDefault($short_name,$id,$hostname,$port) {
$port_details = '<div>
<div>----</div>
<div>----</div>
</div>';
return $port_details;
}
function outputRecords($hostname,$port) {
//get records data from database
$records = $this->getCopy->records($hostname,$port);
//create empty variable for HTML
$records_html = "";
foreach ($records as $key => $value) {
if ($value) {
$records_html .= '<div><span class="copy-group">' .$key. '</span><span class="copy-instance">' .$value. '</span></div>';
} else {
return '<div>Unable to get any group or instance</div>';
}
}
return $records_html;
}
function usbPortDetails($short_name,$id,$hostname,$port) {
$port_info = '';
$port_info .= 'bunch of HTML';
$port_info .= $this->outputRecords($hostname,$port);
$port_info .= 'bunch of HTML';
return $port_info;
}
My best guess as to the problem, is that there is an issue with the way I am returning the HTML or something with output buffering. I actually don't know if I need it within my class, but I've taken it out and issue is the same. I've also tried adding output buffering to index.html.
Here is a snippet of the source HTML. The first discrepancy I notice is that the <div class="server-details"></div> highlighted in blue doesn't belong there, it should be inside <div class="dc-container"></div> and adjacent to the prior <div class="server-details"></div>. After port-data-left should be port-data-right but it's nowhere to be found. I'm almost convinced at this point that there's a missing closing tag somewhere but I can't find it. It's been several years since I seriously did any development :D
EDIT: After further investigation, it appears that the final $port_info is not outputting and may be causing this problem. Is there an issue with $port_info .= $this->outputRecords($hostname,$port); being there?
Since you reuse you object instance using $generate, the constructor and destructor methods are only called once in this script.
The constructor starts a new output buffer using ob_start(), which means everything outputted by the output method will be buffered until you flush it.
The issue is, the flush only happens in the destructor, with ob_end_flush(), which is only executed once, after the last output call.
Because you echo the result of the method and start a buffer at the same time, the output must indeed be weird and some nesting / repetition occurs because the next output still adds to the buffer.
As pointed out in the comment, the easiest solution is to turn off the output buffering in the class.
You also need to clear $port_info at the beginning of the method to make this work, although it should be fine, unless $port_info is a global var?
This was probably not cleared in the initial Class in order to make it work when called several times, to concatenate the results (and buffer them, before outputting them all at once)
A good usage of buffers in the middle of a page is to redirect the output of a function to a variable.
For example, you could have a function that echoes code but you don't want it echoed.
You could then do something like:
Some HTML
<?php
ob_start();
call_to_some_function_that_normally_outputs_code();
$myvar = ob_get_clean();
?>
Rest of the HTML

before waiting a response from other script, echo something on screen in php

I have a structure like:
echo 'something' on x.php;
x.php -> Requests to y.php;
while(x.php -> waiting Response from y.php)
do something;
but it did not work, because server is locked onRequest and after getting response print all
of 'something' on the screen.
Note: It is not like 'loading..' structure that i want, it's completely different.
Edit:
The Request code part is like (does not actual code):
while(5){
echo 'hey hey';
}
$whatIWant = 'wanted string';
$myTopicString = 'topic34593495';
while(strlen($myTopicString)>2){
$url = 'y.php/'.$myString;
$r = request($url);
$response = response($r);
if(strpos($response,$whatIWant))
break;
$myTopicString -= 1;
}
what i want is printing 'hey hey' string on the screen and see it before the request.
It appears you're trying to do something AJAX was built for.
So please check out http://net.tutsplus.com/tutorials/javascript-ajax/5-ways-to-make-ajax-calls-with-jquery/
If your script is heavy and needs some severe loading-time you should consider AJAX. So first show the main screen elements. After that you retrieve data using JavaScript.

PHP XML. IP validation

My Xml looks like this example:
<?xml version="1.0" encoding="UTF-8"?>
<Allvotes>
<vote score="2" ip="116.971.203.221"/>
<vote score="5" ip="32.97.233.5"/>
<vote score="3" ip="212.977.233.225"/>
<vote score="5" ip="2.80.233.225"/>
</Allvotes>
When on my flash website (AS2), somebody press "vote" button, script in PHP getting his IP... What I want is run specyfic function, depends on his IP exist in xml file or not.
If his IP already exist, PHP send message: "ALREADY VOTED!", when IP doesn't exist in XML, then I want to run function which store his vote score and IP in xml.
So far I know that this PHP script not works:
$dom = new DomDocument('1.0', 'UTF-8');
$myXML = "votes.xml";
$s="";
if ($_POST['todo']=="vote"){
$ip=$_SERVER['REMOTE_ADDR'];
$dom->load($myXML);
$allVotes= $dom->getElementsByTagName('vote');
foreach ($allVotes as $vote){
if ($vote->getAttribute('ip')==$ip){
$s.="&msg= Already Voted";
echo $s;
break;
}else{
doOtherStuff
}
}
}
The problem is that this loop fire "doOtherStuff" function when IP is not in first node...
Is there any magic trick to do that?
Why your code does not work
To answer the immediate question: you need to defer the "already voted?" test until you have iterated over all the records:
$alreadyVoted = false;
foreach ($allVotes as $vote){
if ($vote->getAttribute('ip')==$ip){
$alreadyVoted = true;
break;
}
}
if($alreadyVoted) {
$s.="&msg= Already Voted";
echo $s;
}
else {
// other stuff
}
Why you should not do it this way
Storing your data in XML like this is a really inefficient way of doing things. You should move the data store to a database (MySql is typically easiest to set up and work with from PHP).

Requiring an html page but not echoing it

I'm working with json and AJAX in order to get some HTML without refreshing the page. The AJAX call call a function that simplified is like this:
function postHTML($post) { // returns the HTML structure for a post starting from an array of variables
# other stuff
require('post.template.php'); // which is mostly HTML with just few php variables embedded
}
$data = '';
foreach ($posts as $post) {
$data .= postHTML($post);
}
After this I manage the json as follows:
echo json_encode(array('success' => true, 'data' => $data));
Where data should be the HTML structure of each post loaded.
The problem is that when i require the 'post.template.php' file it uses require and it returns the data to Javascript as:
[HTML posts] {"success": true, "data": ""}
How can I get the HTML into a variable and then pass it to json_encode without actually requiring the page (that should still be executed as PHP)?
You can use Output buffering, arround your require, to capture the output, preventing it from being echoed : it'll get stored in memory, and you'll be able to fetch it to a variable.
Basically, this would mean using the following kind of code :
ob_start();
// Output is no longer sent to the standard output,
// but stored in memory
require('post.template.php');
// Fetch the content that has been stored in memory
$content = ob_get_clean();
As a couple of references :
ob_start()
ob_get_clean()
<?php
function postHTML($post) { // returns the HTML structure for a post starting from an array of variables
ob_start();
# other stuff
require('post.template.php'); // which is mostly HTML with just few php variables embedded
$output = ob_get_contents();
ob_end_clean();
return $output;
}
$data = '';
foreach ($posts as $post) {
$data .= postHTML($post);
}
Put all of the data-generating code in another function and call it when necessary. (Yes nested functions are allowed in PHP, but not sure this works in your case since you're using 'require')

Categories