From 699d897d6dbed1d68fb56b51e83fbea303a08785 Mon Sep 17 00:00:00 2001 From: Mark Guinn Date: Mon, 10 Nov 2014 10:14:19 +0000 Subject: [PATCH] initial project state --- LICENSE | 20 ++++++++ README.md | 64 ++++++++++++++++++++++++++ _config.php | 1 + _config/config.yml | 21 +++++++++ composer.json | 23 ++++++++++ src/ClockworkController.php | 28 +++++++++++ src/ClockworkControllerExtension.php | 38 +++++++++++++++ src/DatabaseProxy.php | 68 +++++++++++++++++++++++++++ src/RequestFilter.php | 69 ++++++++++++++++++++++++++++ src/SilverstripeDataSource.php | 43 +++++++++++++++++ 10 files changed, 375 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 _config.php create mode 100644 _config/config.yml create mode 100644 composer.json create mode 100644 src/ClockworkController.php create mode 100644 src/ClockworkControllerExtension.php create mode 100644 src/DatabaseProxy.php create mode 100644 src/RequestFilter.php create mode 100644 src/SilverstripeDataSource.php diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bd0dc3d --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +License (MIT) +------------- +Copyright (c) 2014 Mark Guinn + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, subject +to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..910cc1a --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +SilverStripe Clockwork Integration +================================== + +Integrates the wonderful Clockwork Chrome extension into Silverstripe. Out of +the box queries and controller events will be logged. You can also log your +own events on the timeline. + +* Clockwork: https://github.com/itsgoingd/clockwork +* Chrome Extension: https://github.com/itsgoingd/clockwork-chrome + +NOTE: This extension is only active when your site is in dev mode. There should +be no slowdown in live mode because the database proxy adapter is not installed +and clockwork is never activated. + + +Usage +----- +Install the chrome extension. Install via composer. Basic timing and query +logging work out of the box. To add your own events to the timeline use: + +``` +Injector::inst()->get('ClockworkTimeline')->startEvent('myevent1', 'Description of the event'); + +// ... do the things ... /// + +// this will happen automatically when the request ends +Injector::inst()->get('ClockworkTimeline')->endEvent('myevent1'); +``` + + +Todo List +--------- +- Integrate SS_Log for logging (Debug::log is too janky at this point) +- Integrate the embeddable web version for non-chrome-users: https://github.com/itsgoingd/clockwork-web + + +Developer(s) +------------ +- Mark Guinn + +Contributions welcome by pull request and/or bug report. +Please follow Silverstripe code standards. + + +License (MIT) +------------- +Copyright (c) 2014 Mark Guinn + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, subject +to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/_config.php b/_config.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/_config.php @@ -0,0 +1 @@ + + * @date 11.07.2014 + * @package clockwork + */ + +namespace Clockwork\Support\Silverstripe; +use Clockwork\Storage\FileStorage; +use Controller; +use SS_HTTPRequest; + +class ClockworkController extends Controller +{ + private static $allowed_actions = array('retrieve'); + private static $url_handlers = array('$ID!' => 'retrieve'); + + public function retrieve(SS_HTTPRequest $request) { + $storage = new FileStorage(TEMP_FOLDER . '/clockwork'); + $data = $storage->retrieve($request->param('ID')); + $response = $this->getResponse(); + $response->addHeader('Content-type', 'application/json'); + $response->setBody($data->toJson()); + return $response; + } +} diff --git a/src/ClockworkControllerExtension.php b/src/ClockworkControllerExtension.php new file mode 100644 index 0000000..f748961 --- /dev/null +++ b/src/ClockworkControllerExtension.php @@ -0,0 +1,38 @@ + + * @date 11.07.2014 + * @package salveo + * @subpackage clockwork + */ + +namespace Clockwork\Support\Silverstripe; + +use Extension; +use Injector; +use Director; + +class ClockworkControllerExtension extends Extension +{ + public function onBeforeInit() { + if (Director::isDev()) { + Injector::inst()->get('ClockworkTimeline')->startEvent( + get_class($this->owner) . '_init', + get_class($this->owner) . ' initialization' + ); + } + } + + public function onAfterInit() { + if (Director::isDev()) { + $injector = Injector::inst(); + $injector->get('ClockworkTimeline')->endEvent(get_class($this->owner) . '_init'); + $injector->get('ClockworkTimeline')->startEvent( + get_class($this->owner) . '_action', + get_class($this->owner) . ' action ' . $this->owner->getRequest()->param('Action') + ); + } + } +} diff --git a/src/DatabaseProxy.php b/src/DatabaseProxy.php new file mode 100644 index 0000000..5269e14 --- /dev/null +++ b/src/DatabaseProxy.php @@ -0,0 +1,68 @@ + + * @date 11.07.2014 + * @package clockwork + */ + +namespace Clockwork\Support\Silverstripe; + +use SS_Query; +use SS_Database; + +class DatabaseProxy +{ + /** @var SS_Database */ + protected $realConn; + + /** @var array */ + protected $queries; + + + /** + * @param SS_Database $realConn + */ + public function __construct($realConn) { + $this->realConn = $realConn; + $this->queries = array(); + } + + + /** + * Execute the given SQL query. + * This function must be defined by subclasses as part of the actual implementation. + * It should return a subclass of SS_Query as the result. + * @param string $sql The SQL query to execute + * @param int $errorLevel The level of error reporting to enable for the query + * @return SS_Query + */ + public function query($sql, $errorLevel = E_USER_ERROR) { + $starttime = microtime(true); + $handle = $this->realConn->query($sql, $errorLevel); + $endtime = microtime(true); + $this->queries[] = array('query' => $sql, 'duration' => round(($endtime - $starttime) * 1000.0, 2)); + return $handle; + } + + + /** + * @return array + */ + public function getQueries() { + return $this->queries; + } + + + /** + * @param string $name + * @param array $arguments + * @return mixed + */ + public function __call($name, $arguments) { + return call_user_func_array(array($this->realConn, $name), $arguments); + } + +} diff --git a/src/RequestFilter.php b/src/RequestFilter.php new file mode 100644 index 0000000..9aa49e3 --- /dev/null +++ b/src/RequestFilter.php @@ -0,0 +1,69 @@ + + * @date 11.07.2014 + * @package clockwork + */ +namespace Clockwork\Support\Silverstripe; + +use Clockwork\Clockwork; +use Clockwork\DataSource\PhpDataSource; +use Clockwork\Storage\FileStorage; +use DataModel; +use Session; +use SS_HTTPRequest; +use SS_HTTPResponse; +use Director; +use DB; + +class RequestFilter implements \RequestFilter +{ + protected $clockwork; + + /** + * Filter executed before a request processes + * + * @param SS_HTTPRequest $request Request container object + * @param Session $session Request session + * @param DataModel $model Current DataModel + * @return boolean Whether to continue processing other filters. Null or true will continue processing (optional) + */ + public function preRequest(SS_HTTPRequest $request, Session $session, DataModel $model) { + if (Director::isDev()) { + $this->clockwork = new Clockwork(); + + // Wrap the current database adapter in a proxy object so we can log queries + DB::setConn(new DatabaseProxy(DB::getConn())); + $this->clockwork->addDataSource(new SilverstripeDataSource()); + + // Attach a default datasource that comes with + // the Clockwork library (grabs session info, etc) + $this->clockwork->addDataSource(new PhpDataSource()); + + // Give it a place to store data + $this->clockwork->setStorage(new FileStorage(TEMP_FOLDER . '/clockwork')); + } + } + + + /** + * Filter executed AFTER a request + * + * @param SS_HTTPRequest $request Request container object + * @param SS_HTTPResponse $response Response output object + * @param DataModel $model Current DataModel + * @return boolean Whether to continue processing other filters. Null or true will continue processing (optional) + */ + public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model) { + if (Director::isDev()) { + $response->addHeader("X-Clockwork-Id", $this->clockwork->getRequest()->id); + $response->addHeader("X-Clockwork-Version", Clockwork::VERSION); + $this->clockwork->resolveRequest(); + $this->clockwork->storeRequest(); + } + } +} diff --git a/src/SilverstripeDataSource.php b/src/SilverstripeDataSource.php new file mode 100644 index 0000000..1109a9e --- /dev/null +++ b/src/SilverstripeDataSource.php @@ -0,0 +1,43 @@ + + * @date 11.08.2014 + * @package clockwork + */ + +namespace Clockwork\Support\Silverstripe; + +use Clockwork\DataSource\DataSource; +use Clockwork\Request\Request; +use DB; +use Injector; + +class SilverstripeDataSource extends DataSource +{ + /** + * The entry-point. called by Clockwork itself. + * @param Request $request + * @return \Clockwork\Request\Request + */ + function resolve(Request $request) + { + // Retrieve the timeline + $timeline = Injector::inst()->get('ClockworkTimeline'); + $timeline->finalize(); + $request->timelineData = $timeline->toArray(); + + // Retrieve the query log + $db = DB::getConn(); + if ($db instanceof DatabaseProxy) { + $request->databaseQueries = $db->getQueries(); + } + + return $request; + } +}