So, the following might be absolutely against WordPress standards & best practices, but I'm in the process of learning.
Currently, I am calling a PHP file to generate a PDF through a third party.
The way I call the file is by visiting: .php?order_id=1553&key=d5df1b20-15a9-11e9-ad15-c704673daa98
The code for generate.php:
require_once("../../../wp-load.php");
nocache_headers();
$orderId = $_GET['order_id'];
$key = $_GET['key'];
$eventixOrderId = get_post_meta( $orderId, 'eventix_order_id', true );
if($eventixOrderId != $key){
echo "These are not the tickets you're looking for.";
exit;
}
$accessToken = get_option('eventix_access_token');
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_URL => '.0.0/order/'.$eventixOrderId.'/downloadlink',
CURLOPT_USERAGENT => 'example',
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Authorization: ' . $accessToken)
));
$ticketPdf = curl_exec($curl);
curl_close($curl);
update_post_meta( $orderId, 'eventix_pdf_ticket', $ticketPdf );
header("Location: $ticketPdf");
exit;
Now, for the most part, this actually works excellent. But in a few cases this script messes up, throws me to the 404 page of example and doesn't load the PDF. On the other hand, if I visit the link incognito; no problems.
My first thought was that it might be caused by header cache. I tried numerous ways to clear header cache, but didn't work.
So, the following might be absolutely against WordPress standards & best practices, but I'm in the process of learning.
Currently, I am calling a PHP file to generate a PDF through a third party.
The way I call the file is by visiting: https://example/wp-content/plugins/eventix-woocommerce/generate.php?order_id=1553&key=d5df1b20-15a9-11e9-ad15-c704673daa98
The code for generate.php:
require_once("../../../wp-load.php");
nocache_headers();
$orderId = $_GET['order_id'];
$key = $_GET['key'];
$eventixOrderId = get_post_meta( $orderId, 'eventix_order_id', true );
if($eventixOrderId != $key){
echo "These are not the tickets you're looking for.";
exit;
}
$accessToken = get_option('eventix_access_token');
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_URL => 'https://api.eventix.io/3.0.0/order/'.$eventixOrderId.'/downloadlink',
CURLOPT_USERAGENT => 'example',
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Authorization: ' . $accessToken)
));
$ticketPdf = curl_exec($curl);
curl_close($curl);
update_post_meta( $orderId, 'eventix_pdf_ticket', $ticketPdf );
header("Location: $ticketPdf");
exit;
Now, for the most part, this actually works excellent. But in a few cases this script messes up, throws me to the 404 page of example and doesn't load the PDF. On the other hand, if I visit the link incognito; no problems.
My first thought was that it might be caused by header cache. I tried numerous ways to clear header cache, but didn't work.
Share Improve this question asked Jan 11, 2019 at 14:52 KevsterKevster 331 silver badge9 bronze badges 3- Additional comment: I added header('Content-type: application/pdf'); to the code which seems to have solved the issue. Any comments in regards to the code, and if it's a best practice to do it this way.. much much appreciated. – Kevster Commented Jan 11, 2019 at 14:56
- I was going to mention the Content-type header, but looks like you've done that. Other than that, your code looks OK. I would probably also add the Content-disposition header so you can provide a more consistent user experience. Use "inline" for opening PDF in the browser, or "attachment" for serving a "save as" dialog. i.e. ('Content-disposition: inline'; ). As for best practices, if it doesn't need to be portable, it's OK (although better methods exist). Just be aware that if you move it, the path to wp-load.php will be invalid. – butlerblog Commented Jan 11, 2019 at 15:18
- Changed my mind on what I said about best practices. You really should go ahead and work it in so you don't have to directly hit the file and include wp-load.php. I put in an answer below that will get you started in that direction. It might need a little work. As-is, it is a drop-in code snippet for your theme's functions.php file. But add a proper plugin header and you can load it as a plugin. I'd go the plugin route since that would make it more portable (not theme dependent). – butlerblog Commented Jan 11, 2019 at 15:42
1 Answer
Reset to default 1Here's an approach that would be more portable (so you don't have to call wp-load.php directly, which is something you want to avoid) and also would allow you to revise the URL to not point to the /wp-content/plugins/ directory (also something you want to avoid):
add_action( 'init', 'my_pdf_generator' );
function my_pdf_generator() {
// sniff out query args to see if this is a pdf request.
// Note that you should probably add a unique query arg to make sure this is a ticket request.
// Mabye something like "?eventix_order=true" or something just in case order_id and key exist in
// some other kind of instance.
if ( isset( $_GET['order_id'] ) && isset( $_GET['key'] ) ) {
nocache_headers();
// Don't forget to sanitize
$orderId = $_GET['order_id'];
$key = $_GET['key'];
$eventixOrderId = get_post_meta( $orderId, 'eventix_order_id', true );
if($eventixOrderId != $key){
echo "These are not the tickets you're looking for.";
exit;
}
$accessToken = get_option('eventix_access_token');
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_URL => 'https://api.eventix.io/3.0.0/order/'.$eventixOrderId.'/downloadlink',
CURLOPT_USERAGENT => 'example',
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Authorization: ' . $accessToken)
));
$ticketPdf = curl_exec($curl);
curl_close($curl);
update_post_meta( $orderId, 'eventix_pdf_ticket', $ticketPdf );
header( 'Content-Description: File Transfer' );
header( 'Content-Type: application/pdf' );
header( 'Content-Disposition: inline' );
header( 'Content-Transfer-Encoding: binary' );
header( 'Cache-Control: must-revalidate' );
header( 'Pragma: public' );
header( "Location: $ticketPdf" );
exit();
}
}
Doing it this way would make your link as follows:
https://example/?order_id=1553&key=d5df1b20-15a9-11e9-ad15-c704673daa98
What it's doing is hooking to the 'init' action to check if this is a key request, and if so, it runs your process and exits at that end (so no additional WP output is sent).
That's cleaner than putting it in a custom file, directly calling that custom file, and needing to include wp-load.php.
To use it this way, you can either just drop it in your theme's functions.php file OR add a plugin file header to it and load it as a plugin. I'd recommend loading it as a plugin so that it is portable and not theme dependent, just in case you change things theme-wise down the road.
This would also be a starting point for other learning. This example just uses a raw query arg, but with additional research and work, you could probably learn to build it into a functional WooCommerce endpoint like:
https://example/eventix_order/1553/d5df1b20-15a9-11e9-ad15-c704673daa98/
Obviously, I can't test it as is, so you may need to tweak it to make it fully functional. I also noted in the code comments that I would probably work in an additional query argument that is unique just in case "order_id" and "key" may exist together as query args somewhere else (something like "eventix_order=true" or something). You could add that to the "if" condition.
I also added some additional headers that I think make it cleaner - you may need to experiment and/or revise as needed since as I mentioned, I can't test it exactly. But I have personally built download plugins and this comes from that work, so I think they should be correct for your purpose.