Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

call by php #3

Open
Tesla-Renting opened this issue Aug 17, 2021 · 1 comment
Open

call by php #3

Tesla-Renting opened this issue Aug 17, 2021 · 1 comment

Comments

@Tesla-Renting
Copy link

I created a php script that calls the cgi with multiple files, because I couldn't find any php example. I think maybe other people find this helpful, so maybe it could be included somewhere in the docs.

<?php

class TeXLive
{
    private $files;
    private $boundary;
    private $eol = "\r\n";
    /**
     * The path & filename to save to.
     */
    private $output = 'document.pdf';

    private static function generateBoundary(): string
    {
        return 'TITO-'.md5(time());
    }

    public function __construct(array $files, string $output = 'document.pdf')
    {
        $this->files = $files;
        $this->boundary = self::generateBoundary();
        $this->output = $output;
    }

    private function getPart($name, $value)
    {
        $part = '--'.$this->boundary.$this->eol;
        $part .= 'Content-Disposition: form-data; name="'.$name.'"'.$this->eol;
        // $part .= 'Content-Length: '.strlen($value).$this->eol;
        $part .= $this->eol;
        $part .= $value.$this->eol;

        return $part;
    }

    private function getBody()
    {
        $body = '';
        $body .= $this->getPart('return', 'pdf');
        $body .= $this->getPart('engine', 'pdflatex');
        $body .= $this->getPart('filename[]', 'document.tex');
        $body .= $this->getPart('filecontents[]', file_get_contents($this->files[0]));
        for ($i = 1; $i < count($this->files); ++$i) {
            $filename = $this->files[$i];
            $body .= $this->getPart('filename[]', $filename);
            $body .= $this->getPart('filecontents[]', file_get_contents($filename));
        }

        $body .= '--'.$this->boundary.'--'.$this->eol;

        return $body;
    }

    public function generateDocument()
    {
        $ch = curl_init();
        // curl_setopt($ch, CURLOPT_URL, 'https://postman-echo.com/post');
        curl_setopt($ch, CURLOPT_URL, 'https://texlive.net/cgi-bin/latexcgi');
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt($ch, CURLOPT_HTTPHEADER,
          ['Content-Type: multipart/form-data; boundary='.$this->boundary]);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $this->getBody($this->boundary));
        // header('Content-Type: application/json');
        curl_exec($ch);

        $info = curl_getinfo($ch);
        // echo json_encode($info);

        curl_close($ch);
        $this->saveDocument($info['redirect_url']);
    }

    /**
     * fileUrl: The resource that we want to download.
     */
    private function saveDocument(string $fileUrl)
    {
        $saveTo = $this->output;
        if (endsWith($fileUrl, 'log')) {
            $saveTo = 'TeXLiveError.log';
        }

        //Open file handler.
        $fp = fopen($saveTo, 'w+');

        //If $fp is FALSE, something went wrong.
        if ($fp === false) {
            throw new Exception('Could not open: '.$saveTo);
        }

        //Create a cURL handle.
        $ch = curl_init($fileUrl);

        //Pass our file handle to cURL.
        curl_setopt($ch, CURLOPT_FILE, $fp);

        //Timeout if the file doesn't download after 20 seconds.
        curl_setopt($ch, CURLOPT_TIMEOUT, 20);

        //Execute the request.
        curl_exec($ch);

        //If there was an error, throw an Exception
        if (curl_errno($ch)) {
            throw new Exception(curl_error($ch));
        }

        //Get the HTTP status code.
        $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        //Close the cURL handler.
        curl_close($ch);

        //Close the file handler.
        fclose($fp);

        if ($statusCode != 200) {
            throw new Exception('Error downloading pdf. Status Code: '.$statusCode);
        }
    }
}

// (new TeXLive(['document.tex', 'file1.tex']))->generateDocument();

function endsWith($haystack, $needle)
{
    $length = strlen($needle);
    if (!$length) {
        return true;
    }

    return substr($haystack, -$length) === $needle;
}

How to call

Only the constructor and generateDocument are public, so just look at the interface of these methods.
The files handed over to the constructor should all be in the current working directory, so maybe you have to call chdir before calling generateDocument.

@davidcarlisle
Copy link
Owner

Thanks for this, I may add to the currently undocumented curl directory that already has a shell script for a commandline wrapper around curl. I'm wary about advertising these things too much though. Using the javascript interface with a manual submit button (as at https://learnlatex.org or https://latex.org/forum/ adds a natural hinderence to too many document requests being made in a short time.

Currently the service runs free to the user with no login or registration required, I'd like to keep it that way but if scripted interfaces are being used (php as here or the bash script that I provided) it is easy to generate multiple requests and swamp the server, even if that is not intended to be malicious.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants