Skip to content

Commit

Permalink
Add CallbackContainerBuilder, LoggableContainerBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
mwjames committed Nov 26, 2016
1 parent b15a06c commit 5a64b66
Show file tree
Hide file tree
Showing 22 changed files with 1,076 additions and 131 deletions.
43 changes: 40 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
[![Dependency Status](https://www.versioneye.com/php/onoi:callback-container/badge.png)](https://www.versioneye.com/php/onoi:callback-container)

A simple object instantiator to lazy load registered callback handlers. Part of the
code base has been extracted from [Semantic MediaWiki][smw] and is now being deployed as independent library.
code base has been extracted from [Semantic MediaWiki][smw] and is now being
deployed as independent library.

## Requirements

Expand All @@ -22,13 +23,15 @@ the dependency to your [composer.json][composer].
```json
{
"require": {
"onoi/callback-container": "~1.1"
"onoi/callback-container": "~2.0"
}
}
```

## Usage

### CallbackContainerBuilder and CallbackContainer

```php
class FooCallbackContainer implements CallbackContainer {

Expand Down Expand Up @@ -105,6 +108,34 @@ $anotherServiceFromFile = $containerBuilder->create( 'AnotherServiceFromFile', '
If a callback handler is registered with an expected return type then any
mismatch of a returning instance will throw a `RuntimeException`.

### LoggableContainerBuilder

`LoggableContainerBuilder` is provided as facade to enable logging and sniffing of
registered callback instances.

```php
use Onoi\CallbackContainer\CallbackContainerFactory;

$callbackContainerFactory = new CallbackContainerFactory();

$containerBuilder = $callbackContainerFactory->newLoggableContainerBuilder(
$callbackContainerFactory->newCallbackContainerBuilder(),
$callbackContainerFactory->newBacktraceSniffer( 10 ),
$callbackContainerFactory->newCallFuncMemorySniffer()
);

$containerBuilder->registerCallbackContainer(
new FooCallbackContainer()
);

$containerBuilder->setLogger(
$containerBuilder->singleton( 'SomeLogger' )
);

```

If a `Psr\Log\LoggerInterface` is set then a similar [output](docs/example.loggable.output.md) will be produced.

## Contribution and support

If you want to contribute work to the project please subscribe to the
Expand All @@ -122,6 +153,12 @@ The library provides unit tests that covers the core-functionality normally run

## Release notes

- 2.0.0 (2016-11-26)
- Added `CallbackContainerFactory`
- Added `LoggableContainerBuilder`
- Added `CallbackContainerBuilder::registerFromFile` to allow loading callback
definitions from a file

- 1.1.0 (2016-09-07)
- Added `ServicesManager` as convenience class to manage on-the-fly services independent of
an active `DeferredCallbackLoader` instance
Expand All @@ -131,7 +168,7 @@ The library provides unit tests that covers the core-functionality normally run
- Deprecated the `CallbackLoader` interface in favour of the `CallbackInstantiator` interface
- Deprecated the `NullCallbackLoader` class in favour of the `NullCallbackInstantiator` class

- 1.0.0 Initial release (2015-09-08)
- 1.0.0 (2015-09-08)
- Added the `CallbackContainer` and `CallbackLoader` interface
- Added the `DeferredCallbackLoader` and `NullCallbackLoader` implementation

Expand Down
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
}
],
"require": {
"php": ">=5.3.2"
"php": ">=5.3.2",
"psr/log": "~1.0"
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
"dev-master": "2.x-dev"
}
},
"autoload": {
Expand Down
49 changes: 49 additions & 0 deletions docs/example.loggable.output.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
An example output produced by the `LoggableContainerBuilder`.

```
"NamespaceExaminer": {
"prototype": 3,
"prototype-backtrace": [
[
"require",
"require_once",
"call_user_func",
"{closure}",
"SemanticCite::onExtensionFunction",
"SCI\\HookRegistry::__construct",
"SCI\\HookRegistry::addCallbackHandlers",
"SMW\\ApplicationFactory::getNamespaceExaminer",
"Onoi\\CallbackContainer\\LoggableContainerBuilder::create"
],
[
"OutputPage::addParserOutput",
"OutputPage::addParserOutputMetadata",
"Hooks::run",
"call_user_func_array",
"SMW\\MediaWiki\\Hooks\\HookRegistry::SMW\\MediaWiki\\Hooks\\{closure}",
"SMW\\MediaWiki\\Hooks\\OutputPageParserOutput::process",
"SMW\\MediaWiki\\Hooks\\OutputPageParserOutput::canPerformUpdate",
"SMW\\MediaWiki\\Hooks\\OutputPageParserOutput::isSemanticEnabledNamespace",
"SMW\\ApplicationFactory::getNamespaceExaminer",
"Onoi\\CallbackContainer\\LoggableContainerBuilder::create"
],
[
"SkinTemplate::outputPage",
"SkinTemplate::prepareQuickTemplate",
"Hooks::run",
"call_user_func_array",
"SBL\\HookRegistry::SBL\\{closure}",
"SBL\\SkinTemplateOutputModifier::modifyOutput",
"SBL\\SkinTemplateOutputModifier::canModifyOutput",
"SBL\\SkinTemplateOutputModifier::isEnabled",
"SMW\\ApplicationFactory::getNamespaceExaminer",
"Onoi\\CallbackContainer\\LoggableContainerBuilder::create"
]
],
"singleton": 0,
"singleton-memory": [],
"singleton-time": [],
"prototype-memory-median": 1432,
"prototype-time-median": 0.00018199284871419
},
```
82 changes: 82 additions & 0 deletions src/BacktraceSniffer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

namespace Onoi\CallbackContainer;

/**
* @license GNU GPL v2+
* @since 2.0
*/
class BacktraceSniffer {

/**
* @var integer
*/
private $depth = 1;

/**
* @since 2.0
*
* @param integer $depth
*/
public function __construct( $depth = 1 ) {
$this->depth = $depth;
}

/**
* @see MediaWiki::wfGetCaller
* @since 2.0
*
* @return $string
*/
public function getCaller( $depth = null ) {

$depth = $depth === null ? $this->depth : $depth;
$backtrace = $this->getBackTrace( $depth + 1 );

if ( isset( $backtrace[$depth] ) ) {
return $this->doFormatStackFrame( $backtrace[$depth] );
}

return 'unknown';
}

/**
* @see MediaWiki::wfGetCallers
* @since 2.0
*
* @return array
*/
public function getCallers( $depth = null ) {

$depth = $depth === null ? $this->depth : $depth;
$backtrace = array_reverse( $this->getBackTrace() );


if ( !$depth || $depth > count( $backtrace ) - 1 ) {
$depth = count( $backtrace ) - 1;
}

$backtrace = array_slice( $backtrace, -$depth - 1, $depth );

return array_map( array( $this, 'doFormatStackFrame' ), $backtrace );
}

private function doFormatStackFrame( $frame ) {
return isset( $frame['class'] ) ? $frame['class'] . '::' . $frame['function'] : $frame['function'];
}

private function getBackTrace( $limit = 0 ) {
static $disabled = null;

if ( $disabled || ( $disabled = !function_exists( 'debug_backtrace' ) ) === true ) {
return array();
}

if ( $limit && version_compare( PHP_VERSION, '5.4.0', '>=' ) ) {
return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit + 1 ), 1 );
} else {
return array_slice( debug_backtrace(), 1 );
}
}

}
92 changes: 92 additions & 0 deletions src/CallFuncMemorySniffer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

namespace Onoi\CallbackContainer;

use RuntimeException;

/**
* @see http://stackoverflow.com/questions/19973037/benchmark-memory-usage-in-php
*
* @license GNU GPL v2+
* @since 2.0
*/
class CallFuncMemorySniffer {

/**
* @var integer
*/
private static $max;

/**
* @var integer
*/
private static $memory;

/**
* @var integer
*/
private $time;

/**
* @since 2.0
*/
public static function memoryTick() {
self::$memory = memory_get_usage() - self::$memory;
self::$max = self::$memory > self::$max ? self::$memory : self::$max;
self::$memory = memory_get_usage();
}

/**
* @since 2.0
*
* @param Closure|callable $func
* @param array|null $args
*
* @return mixed
* @throws RuntimeException
*/
public function call( $func, $args = null ) {

if ( !is_callable( $func ) ) {
throw new RuntimeException( "Function is not callable" );
}

declare( ticks=1 );

self::$memory = memory_get_usage();
self::$max = 0;

register_tick_function(
'call_user_func_array',
array( '\Onoi\CallbackContainer\CallFuncMemorySniffer', 'memoryTick' ),
array()
);

$this->time = microtime( true );
$result = is_array( $args ) ? call_user_func_array( $func, $args ): call_user_func( $func );
$this->time = microtime( true ) - $this->time;

unregister_tick_function( 'call_user_func_array' );

return $result;
}

/**
* @since 2.0
*
* @return integer
*/
public function getMemoryUsed() {
return self::$max;
}

/**
* @since 2.0
*
* @return float
*/
public function getTimeUsed() {
return $this->time;
}

}
2 changes: 1 addition & 1 deletion src/CallbackContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface CallbackContainer {
/**
* @since 1.0
*
* @param ContainerBuilder $containerBuilder
* @param ContainerLoader $containerLoader
*/
public function register( ContainerBuilder $containerBuilder );

Expand Down
Loading

0 comments on commit 5a64b66

Please sign in to comment.