dompdf using curl instead of allow_url_fopen

2019-08-26 05:38发布

问题:

I've been working through a problem with DOMPDF, we've built an application using CodeIgniter and needed a feature that allowed printing to PDF.

Locally, DOMPDF worked great. However, on my live server all the images were broken and could not be found, even though it would provide the link to the broken image which would work fine if copy/pasted into the url toolbar.

I did some digging and discovered if in my php.ini settings I enabled allow_url_fopen it would fix the problem, which it did!

However, all my success came crashing down when I discovered that allowing this setting made our server less secure, which cannot happen. We have other secure apps on there.

I saw people hinting towards using a programming language called "Curl" to get this done, rather then enabling allow_url_fopen.

Can anyone give me any advice on how to implement CURL into DOMPDF? Has anyone done this before?

Here is my function that generates the PDF:

public function create_pdf($hash = false)
{

    //hash might be full 12-12, or only front 12 chars. must be 12 or invalid
    if (empty($hash) || (strlen($hash) < 12)) 
    {
        redirect('view_proposal/error/invalid');
    }


    $this->data['display_buttons'] = false;

    $proposal = $this->proposal_model->get_by_hash($hash);

    if (empty($proposal)) 
    {
        redirect('view_proposal/error/notfound');
    }

    $page_break  = '<p style="page-break-after: always"></p>';

    //from proposal hash, get:

    // cover page
    $html = $this->_get_page($hash, 'cover', $proposal);
    $html .= $page_break;

    // contents
    $this->load->model('proposal_block_model');
    $blocks = $this->proposal_block_model->get_many_by('proposal_id', $proposal->id);

    if (!empty($blocks))
    {
        foreach ($blocks as $block) {
            $html .= $this->_get_page($hash, $block->block_id, $proposal);
        }           
    }   

    $html .= $page_break;


    // pricing page
    $html .= $this->_get_page($hash, 'pricing', $proposal);
    $html .= $page_break;

    // terms & conditions
    $html .= $this->_get_page($hash, 'terms', $proposal);
    $html .= $page_break;

    //return $html;

    //status info
    //$html .= $this->_get_page($hash, 'status_info', $proposal);
    //$html .= $page_break;

    //create a pdf version of the output, and set to $html

    $path = 'file';

    $this->load->library('MY_dompdf');
    $this->my_dompdf->pdf_create($html, $path, true);
}

You'll see this function references another function called "_get_page". This collects all the pages in a work proposal that you created, then the create_pdf function is creating the PDF output of it with DOMPDF.

Here is the "_get_page" function if it helps:

    function _get_page($hash, $block_id, $proposal)
{
    $this->data['proposal'] = $proposal;

    $this->data['primary'] = (count(explode('-', $hash)) > 1);

    $proposal->sent_date = (empty($activity)) ? '' : 'Proposal Sent Date '. show_local_time((int)$activity->cdate);
    $proposal->end_date = (empty($proposal->end_date)) ? '' : 'Good Until '. show_local_time((int)$proposal->end_date);

    $proposal->accent_color = ($proposal->header_color == 'FFFFFF') ? '000000' : $proposal->accent_color;

    $blocks = $this->block_model->get_toplevel_by_proposal($proposal->id);

    $block_toc = $this->_block_toc($blocks, $block_id);

    $this->data['prev_page'] = $block_toc['prev'];
    $this->data['next_page'] = $block_toc['next'];




    //render substitution variables
    $this->load->library('proposals_lib');
    $proposal->introduction = $this->proposals_lib->render_string($proposal->introduction, $proposal->id);
    $proposal->terms_conditions = $this->proposals_lib->render_string($proposal->terms_conditions, $proposal->id);
    $proposal->conclusion = $this->proposals_lib->render_string($proposal->conclusion, $proposal->id);


    $this->data['proposal_open'] = in_array($proposal->status, array(PROPOSAL_STATUS_DRAFT, PROPOSAL_STATUS_SENT, PROPOSAL_STATUS_REVIEWED));

    $this->data['css'] = 'css/public/external/pdf_proposal.css';

    switch ($block_id)
    {
        case 'cover':
            $this->data['client'] = $this->client_model->get($proposal->client_id);
            $html = $this->load->view('public/external/proposal_pages/cover_page', $this->data, true);
            break;
        case 'pricing':
            $this->load->model('proposal_pricing_items_model');
            $this->data['price_items'] = $this->proposal_pricing_items_model->order_by('order', 'asc')->order_by('id', 'asc')->as_object()->get_many_by('proposal_id',$proposal->id);
            $html = $this->load->view('public/external/proposal_pages/pricing_page', $this->data, true);
            break;
        case 'terms': //only for pdf
            $this->data['terms'] = $proposal->terms_conditions;
            $html = $this->load->view('public/external/proposal_pages/terms_page', $this->data, true);
            break;
        case 'status': //only for pdf
            $html = $this->load->view('public/external/proposal_pages/status_info_page', $this->data, true);
            break;              

        default:

            $contents = $this->block_model->get_by_proposal($proposal->id);
            $this->data['blocks'] = $this->_get_content($contents, $block_id, $block_toc['next']);

            $this->data['blocks'] = $this->proposals_lib->render_sections($this->data['blocks'], $proposal->id);

            $html = $this->load->view('public/external/proposal_pages/internal_page', $this->data, true);
    }

    return $html;
}

And another function, which is small, called "get_page" which is used to get any page of the proposal:

    //ajax call to get any page 
public function get_page()
{

    $hash       = $this->input->post('proposal_hash', true);
    $block_id   = $this->input->post('page_id', true);

    $proposal = $this->proposal_model->get_by_hash($hash);

    //confirm the page_id (block_id) is a part of the proposal hashed, then get content
    if(is_numeric($block_id) && !$this->block_model->block_in_proposal($block_id, $proposal->id))
        return $this->ajax_output(array('message'=>'The page requested is not part of this proposal.') , FALSE);


    $html = $this->_get_page($hash, $block_id, $proposal);

    $this->ajax_output(array('html'=>$html), TRUE);
}

Here is an example of how one of the images is called:

 <img src="<?php echo base_url();?>uploads/clients/<?= $client->image ?>">

Sorry if I'm pulling at straws here for help, it took far to long for me to discover that the fix was enabling "allow_url_fopen", but now that this can't be the solution I'm back to square one. If the only solution is using CURL then I need some help...

Thanks!

EDIT***

Here is my lame attempt at using CURL for the first time:

class MY_dompdf

{

function pdf_create($html, $filename='', $stream=TRUE) 
{


$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://hidden.com/view_proposal/vFgXKJcNSRns');

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
//$st = curl_getinfo($ch, CURLINFO_HTTP_CODE);  
$html = curl_exec($ch);
curl_close($ch);

    $dompdf = new DOMPDF();
    $dompdf->load_html($html);
    $dompdf->render();
    if ($stream) {
        $dompdf->stream($filename.".pdf");
    } else {
        return $dompdf->output();
    }
}

}

Which spits the error:

Fatal error: Call to a member function prepend_child() on a non-object in C:\UniServer\www\clientsky\application\libraries\dompdf\include\frame_tree.cls.php on line 231