fix CS
[phancap.git] / src / phancap / Adapter / Cutycapt.php
index 173a86c78eaf2f0677188b85ccd2c4e683748122..19657c8e778d0b52a1665700d4b557f7f4592bf0 100644 (file)
@@ -1,19 +1,91 @@
 <?php
+/**
+ * Part of phancap
+ *
+ * PHP version 5
+ *
+ * @category  Tools
+ * @package   Adapter_Cutycapt
+ * @author    Christian Weiske <cweiske@cweiske.de>
+ * @copyright 2014 Christian Weiske
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL v3
+ * @link      http://cweiske.de/phancap.htm
+ */
 namespace phancap;
 
+/**
+ * Screenshot rendering using the "cutycapt" command line tool.
+ *
+ * @category  Tools
+ * @package   Adapter_Cutycapt
+ * @author    Christian Weiske <cweiske@cweiske.de>
+ * @copyright 2014 Christian Weiske
+ * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL v3
+ * @version   Release: @package_version@
+ * @link      http://cweiske.de/phancap.htm
+ */
 class Adapter_Cutycapt
 {
+    /**
+     * Lock file handle
+     * @var resourece
+     */
+    protected $lockHdl;
+
+    /**
+     * Lock file path
+     * @var string
+     */
+    protected $lockFile = null;
+
+    /**
+     * Check if all dependencies are available.
+     *
+     * @return mixed TRUE if all is fine, array with error messages otherwise
+     */
     public function isAvailable()
     {
-        //FIXME: setup check for xvfbrun, cutycapt, convert
+        $old = error_reporting(error_reporting() & ~E_STRICT);
+        $arErrors = array();
+        if (\System::which('xvfb-run') === false) {
+            $arErrors[] = '"xvfb-run" is not installed';
+        }
+        if (\System::which('cutycapt') === false) {
+            $arErrors[] = '"cutycapt" is not installed';
+        }
+        if (\System::which('convert') === false) {
+            $arErrors[] = '"convert" (imagemagick) is not installed';
+        }
+
+        error_reporting($old);
+        if (count($arErrors)) {
+            return $arErrors;
+        }
+
+        return true;
     }
 
+    /**
+     * Render a website screenshot
+     *
+     * @param Image   $img     Image configuration
+     * @param Options $options Screenshot configuration
+     *
+     * @return void
+     * @throws \Exception When something fails
+     */
     public function render(Image $img, Options $options)
     {
+        $format = $options->values['sformat'];
+        if ($format == 'jpg') {
+            $format = 'jpeg';
+        }
+
+        $serverNumber = $this->getServerNumber($options);
         $tmpPath = $img->getPath() . '-tmp';
         $cmd = 'cutycapt'
             . ' --url=' . escapeshellarg($options->values['url'])
-            . ' --out-format=' . escapeshellarg($options->values['sformat'])
+            . ' --out-format=' . escapeshellarg($format)
             . ' --out=' . escapeshellarg($tmpPath)
             . ' --max-wait=10000'
             . ' --min-width=' . $options->values['bwidth'];
@@ -23,13 +95,86 @@ class Adapter_Cutycapt
 
         $xvfbcmd = 'xvfb-run'
             . ' -e /dev/stdout'
-            . ' --server-args="-screen 0, 1024x768x24"';
+            . ' --server-args="-screen 0, 1024x768x24"'
+            . ' --server-num=' . $serverNumber;
         Executor::run($xvfbcmd . ' ' . $cmd);
 
         $this->resize($tmpPath, $img, $options);
     }
 
-    protected function resize($tmpPath, $img, $options)
+    /**
+     * Get a free X server number.
+     *
+     * Each xvfb-run process needs its own free server number.
+     * Needed for multiple parallel requests.
+     *
+     * @return integer Server number
+     */
+    protected function getServerNumber()
+    {
+        //clean stale lock files
+        $this->cleanup();
+
+        $num = 100;
+        $bFound = false;
+        do {
+            ++$num;
+            $f = $this->config->cacheDir . 'tmp-curlycapt-server-' . $num . '.lock';
+            $this->lockHdl = fopen($f, 'w');
+            if (flock($this->lockHdl, LOCK_EX | LOCK_NB)) {
+                $this->lockFile = $f;
+                $bFound = true;
+                break;
+            } else {
+                fclose($this->lockHdl);
+            }
+        } while ($num < 200);
+
+        if (!$bFound) {
+            throw new \Exception('Too many requests running');
+        }
+
+        $this->lockFile = $f;
+        return $num;
+    }
+
+    /**
+     * Unlock lock file and clean up old lock files
+     *
+     * @return void
+     */
+    public function cleanup()
+    {
+        if ($this->lockFile !== null && $this->lockHdl) {
+            flock($this->lockHdl, LOCK_UN);
+            unlink($this->lockFile);
+        }
+
+        $lockFiles = glob(
+            $this->config->cacheDir . 'tmp-curlycapt-server-*.lock'
+        );
+
+        $now = time();
+        foreach ($lockFiles as $file) {
+            if ($now - filemtime($file) > 120) {
+                //delete stale lock file; probably something crashed.
+                unlink($file);
+            }
+        }
+    }
+
+    /**
+     * Convert an image to the given size.
+     *
+     * Target file is the path of $img.
+     *
+     * @param string  $tmpPath Path of image to be scaled.
+     * @param Image   $img     Image configuration
+     * @param Options $options Screenshot configuration
+     *
+     * @return void
+     */
+    protected function resize($tmpPath, Image $img, Options $options)
     {
         if ($options->values['sformat'] == 'pdf') {
             //nothing to resize.
@@ -53,5 +198,17 @@ class Adapter_Cutycapt
         //var_dump($convertcmd);die();
         unlink($tmpPath);
     }
+
+    /**
+     * Set phancap configuration
+     *
+     * @param Config $config Phancap configuration
+     *
+     * @return void
+     */
+    public function setConfig(Config $config)
+    {
+        $this->config = $config;
+    }
 }
 ?>