REST GET with parameter ignored, PHP Symfony 3 Mpdf - php

Working on a REST API for PDF processor using Mpdf(and tfox symfony bundle) on Symfony 3 Framework. I created two GET requests, one with no parameters for testing, and one with the parameter(URL of the HTML file) I want to read and then convert into PDF.
The Generic GET function:
/**
*
* #Rest\Get("/create")
*/
public function createPDFAction(){
$mpdfService = $this->get('tfox.mpdfport');
$html = "<h1> Hello </h1>";
$mpdf = $mpdfService->getMpdf();
$mpdf->WriteHTML($html);
$mpdf->Output();
exit;
}
The Second GET function with parameter:
/**
* #param $htmlSource
* #Rest\Get("/create/{htmlSource}")
*/
public function createPDFFromSourceAction($htmlSource){
$mpdfService = $this->get('tfox.mpdfport');
$html = file_get_contents($htmlSource);
$mpdf = $mpdfService->getMpdf();
$mpdf->WriteHTML($html);
$mpdf->Output();
exit;
}
The problem is, when I call the second function using browser or Postman the first function is always returned instead and I get the PDF with "Hello", if I remove the first GET function, I get error "no route found for GET/create"
I investigated:
The PDF URL is correct, I manually inserted it in first function and worked
No syntax error, I copied the same function without the parameters and worked
The Calls I do are:
http://localhost:8000/create This one works
http://localhost:8000/create?htmlSource=PATH-TO-FILE-LOCALLY This one doesnot work
If I put the PATH-TO-FILE-LOCALLY in function 1 manually it works fine
So I have 2 Questions:
Since I am new to REST and LAMP, should I use GET or others ? My goal is to read the HTML form that the user will fill into a variable and pass it to Mpdf which will convert it into PDF and return that PDF for viewing or download
Why only the first GET function is being read ?
Notes: I am developing on Linux, with PHPStorm, PHP 7, Symfony 3, localhost, the html file I am testing with is on my local machine
Side point: In case this is resolved, I am supposed to upload this to my clients server (which is Apache) - do you have any guides on how to do that and what should be the URLs changed to ?
Thank you all in advance
Updates:
I have changed the functionality to POST methods and it now works fine:
/**
* #Rest\Post("/mPDF/")
*/
public function createPDFAction(Request $request){
$source = $request->get('source');
if($source == ""){
return new View('No Data found', Response::HTTP_NO_CONTENT);
}
$mpdfService = $this->get('tfox.mpdfport');
$html = file_get_contents($source);
$mpdf = $mpdfService->getMpdf();
$mpdf->WriteHTML($html);
$mpdf->Output();
exit;
}
After publishing to Apache production server and some configuration tweaks the site is now live ! - but now I am facing a new issue which I will post a new question for with all the config info I have - basically POST method is returning {
"error": {
"code": 405,
"message": "Method Not Allowed"
}
}

http://localhost:8000/create?htmlSource=PATH-TO-FILE-LOCALLY
("/create/{htmlSource}")
These paths do not match.
First path consists of domain name, and route create, while second path has route "create" + slash + wildcard.
Query parameters are not defined within routing annotation. Instead, access them inside controller, using
public function createPDFFromSourceAction(Request $request)
{
$htmlSource = $request->query->get('htmlSource'); // query string parameter
$somethingElse = $request->request->get('somethingElse'); //POST request parameter
...
}
Symfony will pass Request object inside the controller for you.
As for your other question, GET requests are usually used for things that do not change the state of the application, and POST/PUT/PATCH/DELETE requests change the state. Since you are uploading something, use POST request.
For your 'side note' you should ask another question instead.

Related

BasePath attribute always empty in request object, working on PHPUnit tests

I have a controller has an action that looks something like this:
/**
* #Route("/my_route_path", name="my_route_name")
*/
public function doSomethingAction(Request $request)
{
$myPath = $request->getScheme().'://'.$request->getHttpHost().''.$request->getBasePath();
$data = file_get_contents($myPath. '/data_folder/data.json');
return $this->render('#Entry/my_template.html.twig', array(
'data' => json_decode($data, true)
));
}
And I create a functional test for this controller like this:
/** #test */
public function doSomething_should_success()
{
$client = static::createClient();
$crawler = $client->request('GET', '/my_route_path');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
}
But I can't run the functional test I still get : Failed asserting that 500 is identical to 200
So, after I checked the test.log file I find this error : file_get_contents(http://localhost/data_folder/data.json) : failed to open stream
As now the problem is comming from $request->getBasePath() because always contain empty string but the expected behaviour is return PATH_TO_MY_PROJECT_FOLDER\web in my case must return projects\web_apps\MY_PROJECT_FOLDER_NAME\web
So, the simplified question: why the request object always contain an empty basePath string in the unit test but it works very well on the browser.
The Request object helps you handle the request of a client, that is something like GET /my_route_path plus lots of headers and a server that is directed at.
The web server passes those information on to php and symfony, and symfony will turn this into a Request object. Symfony has usually one entry point, which is public/index.php (symfony 4) or web/app.php (symfony 3) which is assumed to be / or possibly /basePath/ (the basepath will be communicated by the web server and handled by Symfony).
Symfony will generate a Request object, where the basepath is essentially abstracted away, and whenever you generate a url (via Controller::generateUrl) the base path is taken into account. that's why the basepath is important for Requests.
This is actually described pretty well in the comments of the Request's functions:
getBasePath vs getPathInfo.
However, this only concerns the public facing URLs and doesn't have anything to do with how you structure your project and where that project is located, because that's completely irrelevant to the Request (separation of concerns and stuff).
So I guess, you are actually looking for the root directory of your project.
To find the location of your project dir, there is the very base version, where you directly use the PHP magic var __DIR__ which contains the directory the current script file is in, and you can navigate from there. since controllers are usually located such that their path is projectdir/src/Controller/TheController.php a __DIR__.'/../.. would give you the projectdir. However, that's not really clean. The better version:
Depending on the symfony version you're using, you should retrieve the project dir via the ParameterBagInterface (symfony 4)
function doSomethingAction(ParameterBagInterface $params) {
$projectDir = $params->get('kernel.project_dir');
}
or via the container (symfony 3) see also: new in symfony 3.3: A simpler way to get the project root directory
function doSomethingAction() {
$projectDir = $this->getParameter('kernel.project_dir');
}
In my case I had to inyect RequestStack $stackand access the main request, after that my "BasePath" has value. This is because I where in a subrequest and I had to access to the top level of the request.
This post helped me to understood: Symfony2 - get main request's current route in twig partial/subrequest
/**
* #Route("/myroute", name="myroute")
*/
public function myroute(RequestStack $stack)
{
$request = $stack->getMainRequest();
$route = $request->getPathInfo();
}

PHP prints out a buffer string into web-page on certain circuntances?

I don't know how to explain it but, I'm gonna give it a try.
This problem concerns 2 servers, a local and a hosting server. Both servers are running the same PHP version which is 7.0 [with almost same configurations]. And 2 controller actions. And the problem comes from $app->run($input, $out); from the codes below.
I have in my controller that action:
/**
* #Route("/testJson")
*/
public function testJsonAction() {
$app = new \Symfony\Bundle\FrameworkBundle\Console\Application($this->get("kernel"));
$app->setAutoExit(false);
$opt = array("command" =>
"doctrine:generate:entity",
"--entity" => "GuervylEditorBundle:TestOnline",
"--fields" => "kl:string");
$input = new \Symfony\Component\Console\Input\ArrayInput($opt);
$out = new \Symfony\Component\Console\Output\BufferedOutput();
$app->run($input, $out);
$out->fetch();
return new JsonResponse(\json_encode(["a" => "b", "c" => "d"]));
}
Calling this action from the local and hosting server returns "{\u0022a\u0022:\u0022b\u0022,\u0022c\u0022:\u0022d\u0022}" and with Content-Type
application/json which is great, it's the expected result.
Now, here comes the problem:
That almost same code above, I set it inside another class, I call it from another controller action, which passes through 4 methods from different classes to call the method that has the code above [callCommand]
This is the method that implement the code:
public function callCommand($cmd, $opt, &$mykernel = null) {
if ($mykernel == NULL) {
$mykernel = new myKernel("dev", false, __DIR__ . "/../Resources/template_2.8/app");
}
$app = new \Symfony\Bundle\FrameworkBundle\Console\Application($mykernel);
$app->setAutoExit(false);
$opt = array("command" => $cmd) + $opt;
$input = new \Symfony\Component\Console\Input\ArrayInput($opt);
$out = new \Symfony\Component\Console\Output\BufferedOutput();
$app->run($input, $out);
}
From that other controller action, I also return a json content at the end. I can't show the code because it's too big.
When I call that controller action from my localhost, I get the JSON content and Content-Type: application/json which is fine.
But calling it from the hosting server I get extra texts like:
Entity generation
created ./src/Guervyl/EditorBundle/Entity/TestCase.php
> Generating entity class src/Guervyl/EditorBundle/Entity/TestCase.php: OK!
> Generating repository class src/Guervyl/EditorBundle/Repository/TestCaseRepository.php: OK!
Everything is OK! Now get to work :).
Which is the output texts from the console when calling $app->run($input, $out);. After that I get the HTTP header that I set then the json content. And also the content-type is application/x-httpd-php5.
That error only happens on a specific hosting server. I tested other hosting server the code works like on my local server.
My question is why am I getting the error on that specific hosting? Is there something I can change from the PHP.ini to fix it? Because I really need to host my website on that hosting because it offers me great features that I need but the others don't or they are too expensive.
Well, After I debugged the code I noticed that error happened because I did not set the --no-interaction option. So without that option, Symfony was waiting for input when no fields are specified for an Entity.

Display/Download a file from server using Symfony2 and jQuery

Using Symfony2.0 and jQuery
My application generates and stores PDF outside the ./www folder as it is private information (comercial invoices for the purchases within my shop).
I am trying and not getting to allow the customer to download their invoices at any time.
I found this article, but at the beginning it talks about how to get them from the "public" ./www folder. At the end, it offers a piece of code, but I think it is not supported by my version of Symfony:
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class OfflineToolController extends Controller
{
/**
* #return BinaryFileResponse
*/
public function downloadAction()
{
$path = $this->get('kernel')->getRootDir(). "/../downloads/";
$file = $path.'my_file.zip'; // Path to the file on the server
$response = new BinaryFileResponse($file);
// Give the file a name:
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT,'my_file_name.zip');
return $response;
}
}
This is the case: How to create a link to download generated documents in symfony2?
Any idea of how to do this?

Laravel resource controller testing gives NotFoundHttpException the second time

I am encountering a very strange thing while doing testing with Laravel 4. It looks like a bug but there's probably a logical explanation.
I have replicated the "bug" in a clean Laravel install and here's my code:
My resource controller /app/controllers/TestController.php:
(Created with php artisan controller:make TestController)
class TestController extends \BaseController {
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
return Response::json(array());
}
// The end of the file is unchanged
In my app/routes.php:
Route::get('/', function()
{
return View::make('hello');
});
// Note the composed part of the URL.
// My problem isn't present if I just use `myapi` or `path` as a url
Route::resource('myapi/path', 'TestController');
Added in /app/test/ExampleTest.php:
public function testTest()
{
$res = $this->call('GET', 'myapi/path');
// Until here everything works right
$this->assertEquals(json_decode($res->getContent()), array());
// Now I call the same URL a second time
$res = $this->call('GET', 'myapi/path');
$this->assertEquals(json_decode($res->getContent()), array());
}
Now I run phpunit and here's what I get:
There was 1 error:
1) ExampleTest::testTest
Symfony\Component\HttpKernel\Exception\NotFoundHttpException:
/home/me/Web/laraveltest/bootstrap/compiled.php:5531
/home/me/Web/laraveltest/bootstrap/compiled.php:4848
/home/me/Web/laraveltest/bootstrap/compiled.php:4836
/home/me/Web/laraveltest/bootstrap/compiled.php:4828
/home/me/Web/laraveltest/bootstrap/compiled.php:721
/home/me/Web/laraveltest/bootstrap/compiled.php:702
/home/me/Web/laraveltest/vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Client.php:81
/home/me/Web/laraveltest/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Client.php:332
/home/me/Web/laraveltest/vendor/laravel/framework/src/Illuminate/Foundation/Testing/ApplicationTrait.php:51
/home/me/Web/laraveltest/app/tests/ExampleTest.php:25
In my other project I get a slightly different backtrace, but I have the impression that's the same problem: (but I have no idea of why the other is compiled and this one not)
2) UnitModelTest::testOther
Symfony\Component\HttpKernel\Exception\NotFoundHttpException:
/home/me/Web/my-project/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php:148
/home/me/Web/my-project/vendor/laravel/framework/src/Illuminate/Routing/Router.php:1049
/home/me/Web/my-project/vendor/laravel/framework/src/Illuminate/Routing/Router.php:1017
/home/me/Web/my-project/vendor/laravel/framework/src/Illuminate/Routing/Router.php:996
/home/me/Web/my-project/vendor/laravel/framework/src/Illuminate/Foundation/Application.php:775
/home/me/Web/my-project/vendor/laravel/framework/src/Illuminate/Foundation/Application.php:745
/home/me/Web/my-project/vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Client.php:81
/home/me/Web/my-project/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Client.php:327
/home/me/Web/my-project/vendor/laravel/framework/src/Illuminate/Foundation/Testing/ApplicationTrait.php:51
/home/me/Web/my-project/app/tests/UnitModelTest.php:32
In both case the line given in the trace for the test file corresponds to the second call of the test.
As I noted in the comments of the routes.php file, if I use a simple url with no slash, the test passes without problem.
I have no problem when I use the api from the browser.
I found many topics related to the NotFoundHttpException on StackOverflow, but none looks like mine. This one is specifically present when testing and only trigger an error at the second call.
So what am I doing wrong here ? Or is it really a bug ?
The problem is that calls made with the same client will use the provided URI relatively. That means what you actually call is:
myapi/path
myapi/myapi/path
You can fix this if you add a preface the urls with a / to make them absolute to the root of the application.
public function testTest()
{
$res = $this->call('GET', '/myapi/path');
// Until here everything works right
$this->assertEquals(json_decode($res->getContent()), array());
// Now I call the same URL a second time
$res = $this->call('GET', '/myapi/path');
$this->assertEquals(json_decode($res->getContent()), array());
}
If you experience other issues like that it often helps to call
$this->refreshApplication();
(This would also create a new client and therefore solve this issue as well)
In my case this error happened because i change public directory name to public_html my solution was put this in the \App\Providers\AppServiceProvider register method.
public function register()
{
// ...
$this->app->bind('path.public', function() {
return base_path('public_html');
});
}

Codeigniter controller created when image not found

I have a problem in CodeIgniter, and that is that when an image is not found on the server, the instance of a controller is created (besides the one that called the view).
I know all this can sound confusing, so this is the code to observe what I'm saying. I did this changes to a clean 2.1.0 CI version:
Add a controller to override the 404 error page, I added this one:
// add application/controllers/Errors.php
Class Errors extends CI_Controller {
public function error_404() {
echo 'error';
}
}
// change routes.php
$route['404_override'] = 'Errors/error_404';
Use a controller that isn’t the default one with an unexisting image, I used this:
// add application/controllers/Foo.php
Class Foo extends CI_Controller {
public function index() {
echo '<img src="doesntexist.png" />';
}
}
I couldn’t figure out another way of debugging it, so I created a log to write the events on CodeIgniter.php:
// add on CodeIgniter.php line 356
$path = 'log.txt'; //Place log where you can find it
$file = fopen($path, 'a');
fwrite($file, "Calling method {$class}/{$method} with request {$_SERVER['REQUEST_URI']}\r\n");
fclose($file);
With this, the log that generates visiting the index function is the following:
Calling method Foo/index with request /test/index.php/Foo
Calling method Errors/error_404 with request /test/index.php/doesntexist.png
Which is the problem I have, an instance of the Error class is created.
that is that when an image is not found on the server, the instance of a controller is created
Not really. What I believe is happening is that, since you're using a relative path for the image (and calling it directly inside a controller, which is wrong because you're ouputting something before headers), your browser attach the image directly to the CI url, thus making this request to the server:
index.php/doesntexist.png
Which is (correctly) interpreted by CI as a request to a controller, which doesn't exists, and therefore it issues the error class.
You could do, in your actual code (I'd put the images in a view, though):
echo '<img src="/doesntexist.png" />'
using an absoluth path, or using the base_url() method from the url helper:
echo '<img src="'.base_url().'doesntexist.png" />
This should tell the server to fetch the right request (/test/doesntexist.png) and won't trigger that error.

Categories