add benchmark, capture, and debug helpers
authorChuck Hagenbuch <chuck@horde.org>
Tue, 17 Feb 2009 05:51:06 +0000 (00:51 -0500)
committerChuck Hagenbuch <chuck@horde.org>
Tue, 17 Feb 2009 05:51:06 +0000 (00:51 -0500)
framework/View/lib/Horde/View/Helper/Benchmark.php [new file with mode: 0644]
framework/View/lib/Horde/View/Helper/Benchmark/Timer.php [new file with mode: 0644]
framework/View/lib/Horde/View/Helper/Capture.php [new file with mode: 0644]
framework/View/lib/Horde/View/Helper/Capture/Base.php [new file with mode: 0644]
framework/View/lib/Horde/View/Helper/Capture/ContentFor.php [new file with mode: 0644]
framework/View/lib/Horde/View/Helper/Debug.php [new file with mode: 0644]
framework/View/package.xml
framework/View/test/Horde/View/Helper/BenchmarkTest.php [new file with mode: 0644]
framework/View/test/Horde/View/Helper/CaptureTest.php [new file with mode: 0644]
framework/View/test/Horde/View/Helper/DebugTest.php [new file with mode: 0644]

diff --git a/framework/View/lib/Horde/View/Helper/Benchmark.php b/framework/View/lib/Horde/View/Helper/Benchmark.php
new file mode 100644 (file)
index 0000000..8acbc1e
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Measures the execution time of a block in a template and reports the result
+ * to the log.
+ *
+ * Copyright 2007-2008 Maintainable Software, LLC
+ * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage Helper
+ */
+
+/**
+ * Measures the execution time of a block in a template
+ * and reports the result to the log.  Example:
+ *
+ *  <? $bench = $this->benchmark("Notes section") ?>
+ *    <?= $this->expensiveNotesOperation() ?>
+ *  <? $bench->end() ?>
+ *
+ * Will add something like "Notes section (0.34523)" to the log.
+ *
+ * You may give an optional logger level as the second argument
+ * ('debug', 'info', 'warn', 'error').  The default is 'info'.
+ * The level may also be given as a Horde_Log::* constant.
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage Helper
+ */
+class Horde_View_Helper_Benchmark extends Horde_View_Helper_Base
+{
+    /**
+     * Start a new benchmark.
+     *
+     * @param string          $message  Message to log after the benchmark has ended
+     * @param string|integer  $level    Log level to log after the benchmark has ended
+     * @return Horde_View_Helper_Benchmark_Timer
+     */
+    public function benchmark($message = 'Benchmarking', $level = 'info')
+    {
+        return new Horde_View_Helper_Benchmark_Timer($message, $level, $this->_view->logger);
+    }
+
+}
diff --git a/framework/View/lib/Horde/View/Helper/Benchmark/Timer.php b/framework/View/lib/Horde/View/Helper/Benchmark/Timer.php
new file mode 100644 (file)
index 0000000..307b1fa
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+/**
+ * An instance of this class is returned by
+ * Horde_View_Helper_Benchmark::benchmark().
+ *
+ * Copyright 2007-2008 Maintainable Software, LLC
+ * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage Helper
+ */
+
+/**
+ * An instance of this class is returned by
+ * Horde_View_Helper_Benchmark::benchmark().
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage Helper
+ */
+class Horde_View_Helper_Benchmark_Timer
+{
+    /**
+     * Time that the benchmark was started
+     *
+     * @var float  microtime
+     */
+    private $_start;
+
+    /**
+     * Logger instance that will be used to record the
+     * time after the benchmark has ended
+     *
+     * @var null|Horde_Log_Logger
+     */
+    private $_logger;
+
+    /**
+     * Message to log after the benchmark has ended
+     *
+     * @var string
+     */
+    private $_message;
+
+    /**
+     * Log level to log after the benchmark has ended
+     *
+     * @var string|integer
+     */
+    private $_level;
+
+    /**
+     * Start a new benchmark.
+     *
+     * @param string                 $message  Message to log after the benchmark has ended
+     * @param string|integer         $level    Log level to log after the benchmark has ended
+     * @param null|Horde_Log_Logger  $logger   Logger instance or NULL if none is available
+     */
+    public function __construct($message, $level = 'info', $logger = null)
+    {
+        $this->_message = $message;
+        $this->_level   = $level;
+        $this->_logger  = $logger;
+        $this->_start   = microtime(true);
+    }
+
+    /**
+     * End the benchmark and log the result.
+     */
+    public function end()
+    {
+        if ($this->_logger) {
+            // compute elapsed time & build message
+            $elapsed = microtime(true) - $this->_start;
+            $message = sprintf("{$this->_message} (%.5f)", $elapsed);
+
+            // log message (level may be specified as integer or string)
+            if (is_integer($this->_level)) {
+                $this->_logger->log($message, $this->_level);
+            } else {
+                $this->_logger->{$this->_level}($message);
+            }
+        }
+    }
+
+}
diff --git a/framework/View/lib/Horde/View/Helper/Capture.php b/framework/View/lib/Horde/View/Helper/Capture.php
new file mode 100644 (file)
index 0000000..e38d673
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Copyright 2007-2008 Maintainable Software, LLC
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage Helper
+ */
+
+/**
+ * Capture lets you extract parts of code which can be used in other points of
+ * the template or even layout file.
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage Helper
+ */
+class Horde_View_Helper_Capture extends Horde_View_Helper_Base
+{
+    /**
+     * Capture allows you to extract a part of the template into an
+     * instance variable. You can use this instance variable anywhere
+     * in your templates and even in your layout.  Example:
+     *
+     *  <? $capture = $this->capture() ?>
+     *    Welcome To my shiny new web page!
+     *  <? $this->greeting = $capture->end() ?>
+     *
+     * @return Horde_View_Helper_Capture_Base
+     */
+    public function capture()
+    {
+        return new Horde_View_Helper_Capture_Base();
+    }
+
+    /**
+     * Calling contentFor() stores the block of markup for later use.
+     * Subsequently, you can retrieve it inside an instance variable
+     * that will be named "contentForName" in another template
+     * or in the layout.  Example:
+     *
+     *    <? $capture = $this->contentFor("header") %>
+     *      <script type="text/javascript"> alert('hello world') </script>
+     *    <? $capture->end() %>
+     *
+     * You can then use $this->contentForHeader anywhere in your templates:
+     *
+     *    <?= $this->contentForHeader ?>
+     *
+     * @param string $name  Name of the content that becomes the instance
+     *                      variable name. "foo" -> "$this->contentForFoo"
+     * @return Horde_View_Helper_Capture_ContentFor
+     */
+    public function contentFor($name)
+    {
+        return new Horde_View_Helper_Capture_ContentFor($name, $this->_view);
+    }
+
+}
diff --git a/framework/View/lib/Horde/View/Helper/Capture/Base.php b/framework/View/lib/Horde/View/Helper/Capture/Base.php
new file mode 100644 (file)
index 0000000..13a3420
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Copyright 2007-2008 Maintainable Software, LLC
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage Helper
+ */
+
+/**
+ * An instance of this class is returned by
+ * Horde_View_Helper_Capture::capture().
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage Helper
+ */
+class Horde_View_Helper_Capture_Base
+{
+    /**
+     * Are we currently buffering?
+     *
+     * @var boolean
+     */
+    protected $_buffering = true;
+
+    /**
+     * Start capturing.
+     */
+    public function __construct()
+    {
+        ob_start();
+    }
+
+    /**
+     * Stop capturing and return what was captured.
+     *
+     * @return string
+     * @throws Horde_View_Exception
+     */
+    public function end()
+    {
+        if ($this->_buffering) {
+            $this->_buffering = false;
+            $output = ob_get_clean();
+            return $output;
+        } else {
+            throw new Horde_View_Exception('Capture already ended');
+        }
+    }
+
+}
diff --git a/framework/View/lib/Horde/View/Helper/Capture/ContentFor.php b/framework/View/lib/Horde/View/Helper/Capture/ContentFor.php
new file mode 100644 (file)
index 0000000..8107292
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Copyright 2007-2008 Maintainable Software, LLC
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage Helper
+ */
+
+/**
+ * An instance of this class is returned by
+ * Horde_View_Helper_Capture::contentFor().
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage Helper
+ */
+class Horde_View_Helper_Capture_ContentFor extends Horde_View_Helper_Capture_Base
+{
+    /**
+     * Name that will become "$this->contentForName"
+     *
+     * @var string
+     */
+    private $_name;
+
+    /**
+     * Start capturing content that will be stored as
+     * $view->contentForName.
+     *
+     * @param string $name  Name of the content that becomes the instance
+     *                      variable name. "foo" -> "$this->contentForFoo"
+     * @param Horde_View_Base $view
+     */
+    public function __construct($name, $view)
+    {
+        $this->_name = $name;
+        $this->_view = $view;
+        parent::__construct();
+    }
+
+    /**
+     * Stop capturing content and store it in the view.
+     */
+    public function end()
+    {
+        $name = 'contentFor' . ucfirst($this->_name);
+        $this->_view->$name = parent::end();
+    }
+
+}
diff --git a/framework/View/lib/Horde/View/Helper/Debug.php b/framework/View/lib/Horde/View/Helper/Debug.php
new file mode 100644 (file)
index 0000000..746f8c9
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Copyright 2007-2008 Maintainable Software, LLC
+ * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage Helper
+ */
+
+/**
+ * Dumps a variable for inspection.
+ * Portions borrowed from Paul M. Jones' Solar_Debug
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage Helper
+ */
+class Horde_View_Helper_Debug extends Horde_View_Helper_Base
+{
+    /**
+     * Dumps a variable for inspection.
+     *
+     * @param   string  $var
+     * @return  string
+     */
+    public function debug($var)
+    {
+        return '<pre class="debug_dump">'
+             . htmlspecialchars($this->_fetch($var))
+             . '</pre>';
+    }
+
+    /**
+     * Returns formatted output from var_dump().
+     *
+     * Buffers the var_dump output for a variable and applies some
+     * simple formatting for readability.
+     *
+     * @param  mixed   $var   variable to dump
+     * @return string         formatted results of var_dump()
+     */
+    private function _fetch($var)
+    {
+        ob_start();
+        var_dump($var);
+        $output = ob_get_clean();
+        $output = preg_replace("/\]\=\>\n(\s+)/m", "] => ", $output);
+        return $output;
+    }
+
+}
index 282d6c2..ba34676 100644 (file)
@@ -40,7 +40,17 @@ http://pear.php.net/dtd/package-2.0.xsd">
      <dir name="View">
       <dir name="Helper">
        <file name="Base.php" role="php" />
+       <dir name="Benchmark">
+        <file name="Timer.php" role="php" />
+       </dir> <!-- /lib/Horde/View/Helper/Benchmark -->
+       <file name="Benchmark.php" role="php" />
        <file name="Block.php" role="php" />
+       <dir name="Capture">
+        <file name="Base.php" role="php" />
+        <file name="ContentFor.php" role="php" />
+       </dir> <!-- /lib/Horde/View/Helper/Capture -->
+       <file name="Capture.php" role="php" />
+       <file name="Debug.php" role="php" />
        <file name="Javascript.php" role="php" />
        <file name="Tag.php" role="php" />
        <file name="Url.php" role="php" />
@@ -67,7 +77,13 @@ http://pear.php.net/dtd/package-2.0.xsd">
  <phprelease>
   <filelist>
    <install name="lib/Horde/View/Helper/Base.php" as="Horde/View/Helper/Base.php" />
+   <install name="lib/Horde/View/Helper/Benchmark/Timer.php" as="Horde/View/Helper/Benchmark/Timer.php" />
+   <install name="lib/Horde/View/Helper/Benchmark.php" as="Horde/View/Helper/Benchmark.php" />
    <install name="lib/Horde/View/Helper/Block.php" as="Horde/View/Helper/Block.php" />
+   <install name="lib/Horde/View/Helper/Capture/Base.php" as="Horde/View/Helper/Capture/Base.php" />
+   <install name="lib/Horde/View/Helper/Capture/ContentFor.php" as="Horde/View/Helper/Capture/ContentFor.php" />
+   <install name="lib/Horde/View/Helper/Capture.php" as="Horde/View/Helper/Capture.php" />
+   <install name="lib/Horde/View/Helper/Debug.php" as="Horde/View/Helper/Debug.php" />
    <install name="lib/Horde/View/Helper/Javascript.php" as="Horde/View/Helper/Javascript.php" />
    <install name="lib/Horde/View/Helper/Tag.php" as="Horde/View/Helper/Tag.php" />
    <install name="lib/Horde/View/Helper/Url.php" as="Horde/View/Helper/Url.php" />
diff --git a/framework/View/test/Horde/View/Helper/BenchmarkTest.php b/framework/View/test/Horde/View/Helper/BenchmarkTest.php
new file mode 100644 (file)
index 0000000..f84bb7d
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Copyright 2007-2008 Maintainable Software, LLC
+ * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage UnitTests
+ */
+
+/**
+ * @group      view
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage UnitTests
+ */
+class Horde_View_Helper_BenchmarkTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        $this->view = new Horde_View();
+        $this->view->addHelper(new Horde_View_Helper_Benchmark($this->view));
+
+        $log = new Horde_Log_Logger($this->mock = new Horde_Log_Handler_Mock());
+        $this->view->logger = $log;
+    }
+
+    public function testWithoutLogger()
+    {
+        $this->view = new Horde_View();
+        $this->view->addHelper(new Horde_View_Helper_Benchmark($this->view));
+
+        $bench = $this->view->benchmark();
+        $bench->end();
+    }
+
+    public function testDefaults()
+    {
+        $bench = $this->view->benchmark();
+        $bench->end();
+        $this->assertEquals(1, count($this->mock->events));
+        $this->assertLastLogged();
+    }
+
+    public function testWithMessage()
+    {
+        $bench = $this->view->benchmark('test_run');
+        $bench->end();
+        $this->assertEquals(1, count($this->mock->events));
+        $this->assertLastLogged('test_run');
+    }
+
+    public function testWithMessageAndLevelAsString()
+    {
+        $bench = $this->view->benchmark('debug_run', 'debug');
+        $bench->end();
+        $this->assertEquals(1, count($this->mock->events));
+        $this->assertLastLogged('debug_run', 'debug');
+    }
+
+    public function testWithMessageAndLevelAsInteger()
+    {
+        $bench = $this->view->benchmark('debug_run', Horde_Log::DEBUG);
+        $bench->end();
+        $this->assertEquals(1, count($this->mock->events));
+        $this->assertLastLogged('debug_run', 'debug');
+    }
+
+    public function assertLastLogged($message = 'Benchmarking', $level = 'info')
+    {
+        $last = end($this->mock->events);
+        $this->assertEquals(strtoupper($level), $last['levelName']);
+        $this->assertRegExp("/^$message \(.*\)$/", $last['message']);
+    }
+
+}
diff --git a/framework/View/test/Horde/View/Helper/CaptureTest.php b/framework/View/test/Horde/View/Helper/CaptureTest.php
new file mode 100644 (file)
index 0000000..59e9c9e
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Copyright 2007-2008 Maintainable Software, LLC
+ * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage UnitTests
+ */
+
+/**
+ * @group      view
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage UnitTests
+ */
+class Horde_View_Helper_CaptureTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        $this->view   = new Horde_View();
+        $this->helper = new Horde_View_Helper_Capture($this->view);
+    }
+
+    public function testCapture()
+    {
+        $capture = $this->helper->capture();
+        echo $expected = '<span>foo</span>';
+
+        $this->assertEquals($expected, $capture->end());
+    }
+
+    public function testCaptureThrowsWhenAlreadyEnded()
+    {
+        $capture = $this->helper->capture();
+        $capture->end();
+
+        try {
+            $capture->end();
+            $this->fail();
+        } catch (Exception $e) {
+            $this->assertType('Horde_View_Exception', $e);
+            $this->assertRegExp('/capture already ended/i', $e->getMessage());
+        }
+    }
+
+    public function testContentFor()
+    {
+        $capture = $this->helper->contentFor('foo');
+        echo $expected = '<span>foo</span>';
+        $capture->end();
+
+        $this->assertEquals($expected, $this->view->contentForFoo);
+    }
+
+}
diff --git a/framework/View/test/Horde/View/Helper/DebugTest.php b/framework/View/test/Horde/View/Helper/DebugTest.php
new file mode 100644 (file)
index 0000000..9922fe7
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Copyright 2007-2008 Maintainable Software, LLC
+ * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage UnitTests
+ */
+
+/**
+ * @group      view
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage UnitTests
+ */
+class Horde_View_Helper_DebugTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        $this->helper = new Horde_View_Helper_Debug(new Horde_View());
+    }
+
+    // test truncate
+    public function testDebug()
+    {
+        $expected = '<pre class="debug_dump">string(7) &quot;foo&amp;bar&quot;';
+        $this->assertContains($expected, $this->helper->debug('foo&bar'));
+    }
+
+}