Permalink
Browse files

Object: helper for protecting method from multiple invocation

  • Loading branch information...
1 parent eefd645 commit 8bed815fc6dc6071d6177cb25fe1db3927e512c8 @fprochazka committed May 10, 2012
Showing with 81 additions and 0 deletions.
  1. +12 −0 Nette/common/Object.php
  2. +25 −0 Nette/common/ObjectMixin.php
  3. +44 −0 tests/Nette/common/Object.freezeMethod.phpt
View
12 Nette/common/Object.php
@@ -70,6 +70,18 @@
/**
+ * Protect method from repeated invocation.
+ * @param string method name
+ * @throws MemberAccessException
+ */
+ protected function freezeMethod()
+ {
+ ObjectMixin::freezeMethod($this);
+ }
+
+
+
+ /**
* Call to undefined method.
* @param string method name
* @param array arguments
View
25 Nette/common/ObjectMixin.php
@@ -25,6 +25,9 @@
/** @var array */
private static $methods;
+ /** @var array */
+ private static $frozenMethods = array();
+
/**
@@ -241,4 +244,26 @@ public static function has($_this, $name)
return isset(self::$methods[$class]['get' . $name]) || isset(self::$methods[$class]['is' . $name]);
}
+
+
+ /**
+ * Ensures, that the desired method will be called only once.
+ * @param object instance that should be protected
+ */
+ public static function freezeMethod($object)
+ {
+ $frozen =& self::$frozenMethods[spl_object_hash($object)];
+ foreach (debug_backtrace() as $call) {
+ if (isset($call['object']) && $call['object'] === $object && $call['class'] !== 'Nette\Object') {
+ if (!empty($frozen[$call['function']])) {
+ $method = $call['class'] . '->' . $call['function'] . '()';
+ throw new MemberAccessException("Method $method can be called only once.");
+ }
+
+ $frozen[$call['function']] = TRUE;
+ return;
+ }
+ }
+ }
+
}
View
44 tests/Nette/common/Object.freezeMethod.phpt
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * Test: Nette\Object freeze method.
+ *
+ * @author Filip Procházka
+ * @package Nette
+ * @subpackage UnitTests
+ */
+
+
+
+
+require __DIR__ . '/../bootstrap.php';
+
+
+
+class TestClass extends Nette\Object
+{
+ public $context;
+
+ public function injectContext(Nette\DI\Container $context)
+ {
+ $this->freezeMethod();
+ $this->context = $context;
+ }
+}
+
+$context = new Nette\DI\Container;
+
+$obj = new TestClass;
+$obj->injectContext($context);
+
+// when called once, everything should be fine
+Assert::same( $context, $obj->context );
+
+// throw when called twice
+Assert::throws(function() use ($obj, $context) {
+ $obj->injectContext($context);
+}, 'Nette\MemberAccessException', 'Method TestClass->injectContext() can be called only once.');
+
+// it can be called again on another object
+$obj = new TestClass;
+$obj->injectContext($context);

0 comments on commit 8bed815

Please sign in to comment.