Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Object: helper for protecting method from multiple invocation #646

Closed
wants to merge 1 commit into from

3 participants

@fprochazka

Just a helper, for inject*() methods.

Any ideas? Related to [#638]

@fprochazka fprochazka commented on the diff
Nette/common/ObjectMixin.php
@@ -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) {

It would be nice, if it could be done without this function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@dg
Owner
dg commented

I think it could be ensured only by contract. Method inject() is de facto an extension for method __construct() and constructor can also be called multiple times but nobody wants to check it.

@juzna juzna commented on the diff
tests/Nette/common/Object.freezeMethod.phpt
((9 lines not shown))
+ */
+
+
+
+
+require __DIR__ . '/../bootstrap.php';
+
+
+
+class TestClass extends Nette\Object
+{
+ public $context;
+
+ public function injectContext(Nette\DI\Container $context)
+ {
+ $this->freezeMethod();
@juzna
juzna added a note

code: "freezeMethod()"
me: "wtf, which method"?
phpdoc: "the desired method"
me: "wtf, which desired, me didn't pass anything"

I'd rather be explicit and pass the __FUNCTION__ argument there ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@fprochazka

@dg I can live with that. Contract is fine for me, but there are people, that are gonna complain about "proprietary conventions" ;)

@dg
Owner
dg commented

I do not like proprietary conventions too, so ideal solution is to add suitable phpDoc for injection method, something like "call after __construct and never more".

@fprochazka

Ok, this can be closed than.

@fprochazka fprochazka closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
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) {

It would be nice, if it could be done without this function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ 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();
@juzna
juzna added a note

code: "freezeMethod()"
me: "wtf, which method"?
phpdoc: "the desired method"
me: "wtf, which desired, me didn't pass anything"

I'd rather be explicit and pass the __FUNCTION__ argument there ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ $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);
Something went wrong with that request. Please try again.