Documentation page: Using PD4ML with Zend Framework 2 (ZF2). This page is also available as Markdown at using-pd4ml-with-zend-framework-2-zf2.md.

Using PD4ML with Zend Framework 2 (ZF2)

This page describes the PD4ML v3-era approach. To use PD4ML from PHP today, install PD4ML for PHP with Composer (composer require pd4ml/pd4ml-php): it exposes the complete PD4ML Java API to PHP, downloads PD4ML itself, and needs no Pd4Cmd command lines.

This walkthrough adds a "Page as PDF" button to a Zend Framework 2 view, using the Zend Framework tutorial's own Albums sample application as the base. It builds directly on the PHP Wrapper pattern -- shelling out to the Pd4Cmd command-line tool from a small PHP script -- so it's worth reading that page first if the general approach is unfamiliar.

Pd4Cmd lives in the com.pd4ml.tools package, so a launch as java ... -cp $jar Pd4Cmd ... with the bare class name fails. The jar's manifest declares com.pd4ml.tools.Pd4Cmd as its Main-Class, so the simplest form -- the one PD4ML's command-line reference documents -- launches the jar directly with -jar. Both PHP scripts below use it.

Prerequisites

Follow the Zend Framework 2 tutorial far enough to get the Albums demo application running.

Then set up PD4ML's PHP wrapper against that application, as described in PHP Wrapper: download the wrapper package, unpack it into the application root, confirm a Java runtime is reachable from PHP, and run the bundled environment probe script to confirm everything is wired up. The unpacked wrapper should sit alongside the ZF2 application like this:

PD4ML PHP wrapper unpacked next to the ZF2 Albums application directory structure

Adding a "Page as PDF" button to a view

The example adds a "Page as PDF" button to the album list. As a feature this is mostly illustrative -- in a real deployment, you'd typically point the PD4ML converter directly at a known, print-optimized report or invoice URL rather than working out the current page's URL through an HTTP referrer, as this example does for generality.

1. Add a PDF-generation link to the album list view, module/Album/view/album/album/index.phtml:

...
</tr>
<?php endforeach; ?>
<!-- edit begin -->
<tr style="pd4ml-display: none">
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>
<a href="<?php echo $this->url('album', array('action'=>'pdf'));?>">Page as PDF</a>
</td>
</tr>
<!-- edit end -->
</table>

(The added section sits between the edit begin/edit end comments.) The pd4ml-display: none style hides the link itself from the generated PDF -- confirmed still supported in the current PD4ML CSS engine -- since a self-referential "convert to PDF" link makes no sense inside its own PDF output.

With the link in place, the album list looks like this, pointing at a new, not-yet-handled pdf action:

Album list view with the added "Page as PDF" link

2. Add the pdf action handler to module/Album/src/Album/Controller/AlbumController.php:

public function pdfAction()
{
	$url = $this->getRequest()->getHeader('Referer')->getUri();
	$enc = base64_encode($url);
	$this->redirect()->toUrl($this->getRequest()->getBaseUrl() . '/pd4ml.php?url=' . $enc);
}

The handler reads the referring page's URL -- the album list, in this case -- and forwards it to the converter script as a url parameter, base64-encoding it to sidestep any issues with special characters.

3. Create the converter script, public/pd4ml.php, which the redirect above targets:

<?php
$evaluation = 1;
$java = "java";

$url = base64_decode($_GET['url']) . '?layout=pdf';

if ( $evaluation == 1 ) {
	$jar = "../pd4ml_php_wrapper/pd4ml_demo.jar";
} else {
	$jar = "../pd4ml_php_wrapper/pd4ml.jar";
}

header("Pragma: cache");
header("Expires: 0");
header("Cache-control: private");
header('Content-type: application/pdf');
header('Content-disposition: inline');

if ( strpos(php_uname(), 'Windows' ) !== FALSE) {
	// server platform: Windows
	$jar = preg_replace('/\//', "\\\\", $jar);
	$cmdline = "$java -Xmx512m -jar $jar \"$url\" 800 A4";
} else {
	// server platform: UNIX-derived
	$cmdline = "$java -Xmx512m -Djava.awt.headless=true -jar $jar \"$url\" 800 A4";
}

// for the full current flag reference, see the Pd4Cmd Command-Line Reference page

passthru( $cmdline );

?>

Make sure there's no whitespace before <?php or after ?>.

The script decodes the url parameter, converts that URL to a PDF via Pd4Cmd, and streams the resulting bytes back as the HTTP response. It also appends a ?layout=pdf parameter to the URL before conversion, which the next step uses to signal the application to switch to a PDF-optimized layout.

4. Add a PDF-optimized layout template. The original album list view includes a navigation bar, which belongs in an interactive web page but adds nothing useful to a printed/PDF document. Copy module/Application/view/layout/layout.phtml to blank.phtml in the same directory, and strip the navigation bar out:

<?php echo $this->doctype(); ?>

<html lang="en">
<head>
<meta charset="utf-8">
<?php echo $this->headTitle('ZF2 '. $this->translate('Skeleton Application'))->setSeparator(' - ')->setAutoEscape(false) ?>

<?php echo $this->headMeta()->appendName('viewport', 'width=device-width, initial-scale=1.0') ?>

<!-- Le styles -->
<?php echo $this->headLink(array('rel' => 'shortcut icon', 'type' => 'image/vnd.microsoft.icon', 'href' => $this->basePath() . '/images/favicon.ico'))
->appendStylesheet($this->basePath() . '/css/bootstrap.min.css')
->appendStylesheet($this->basePath() . '/css/style.css')
->appendStylesheet($this->basePath() . '/css/bootstrap-responsive.min.css') ?>

<!-- Scripts -->
<?php echo $this->headScript()->appendFile($this->basePath() . '/js/html5.js', 'text/javascript', array('conditional' => 'lt IE 9',))
->appendFile($this->basePath() . '/js/jquery-1.7.2.min.js') ?>

</head>

<body>
<div class="container">
<br>
<?php echo $this->content; ?>
</div>
</body>
</html>

5. Register the new template in module/Album/config/module.config.php:

'view_manager' => array(
	'template_path_stack' => array(
		'album' => __DIR__ . '/../view',
	),
),
// edit begin
'template_map' => array(
	'layout/blank' => __DIR__ . '/../view/layout/blank.phtml',
),
// edit end

6. Switch templates based on the layout query parameter, in module/Album/src/Album/Controller/AlbumController.php's index action:

public function indexAction()
{
	// edit begin
	$flag = $this->params()->fromQuery('layout');
	if ('pdf' == $flag) {
		$layout = $this->layout();
		$layout->setTemplate('layout/blank');
	}
	// edit end

	return new ViewModel(array(
		'albums' => $this->getAlbumTable()->fetchAll(),
	));
}

With all six steps in place, clicking "Page as PDF" produces a navigation-free PDF like this example result.

The complete application structure (excluding the PD4ML PHP wrapper itself, which is covered separately in PHP Wrapper) is available as a downloadable archive. Remember to adjust the database credentials in config/autoload/local.php before running it.

See also

  • PHP Wrapper -- the general PD4ML-from-PHP pattern this walkthrough builds on, including deployment and troubleshooting notes.
  • Pd4Cmd Command-Line Reference -- the full, current flag reference for the Pd4Cmd invocation used by pd4ml.php above.