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:

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> </td>
<td> </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:

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
Pd4Cmdinvocation used bypd4ml.phpabove.
