There's been much discussion recently about what NULL means in the context of, er, context. To try and get everyone on the same page, I'm going to try and summarize the problem space with examples to try and generate further discussion. I apologize if this is a bit long. :-) For the sake of argument I'm going to ignore ArrayAccess, as if we end up with an API that's not compatible with it then we will have to drop it anyway.
Consider the case where the request URL is foo/bar?stuff=things
Now, the bottom-most context object A has a handler bound to 'http:query' that is responsible for returning the query data. There is also another context object B stacked on top of it that does not change anything about the http information.
A caller now requests http:query:stuff. The logic is as follows:
Object B: Is there a value at http:query:stuff? No
Object B: Is there a handler at http:query:stuff? No
Object B: Is there a value at http:query? No
Object B: Is there a handler at http:query? No
Object B: Is there a value at http? No
Object B: Is there a handler at http? No
Object A: Is there a value at http:query:stuff? No
Object A: Is there a handler at http:query:stuff? No
Object A: Is there a value at http:query? No
Object A: Is there a handler at http:query? Yes!
Ojbect A: Call the handler, get back "things". Save that value to http:query:stuff, then return it.
Object B: Cache "things" to http:query:stuff here, too. Return it to the caller.
The caller gets back "things", and does whatever it is going to do. While it looks like a lot of operations most of those are if (isset()) calls, so they're pretty darned cheap.
Now the caller asks for http:query:bob. The process is similar:
Object B: Is there a value at http:query:bob? No
Object B: Is there a handler at http:query:bob? No
Object B: Is there a value at http:query? No
Object B: Is there a handler at http:query? No
Object B: Is there a value at http? No
Object B: Is there a handler at http? No
Object A: Is there a value at http:query:bob? No
Object A: Is there a handler at http:query:bob? No
Object A: Is there a value at http:query? No
Object A: Is there a handler at http:query? No
Object A: Is there a value at http? No
Object A: Is there a handler at http? No
Object A: Return NULL
Object B: Return NULL
The caller gets back NULL, and does whatever it does when it gets NULL.
Now, another context object C, is added to the stack. The caller explicitly assigns NULL for http:query:stuff, then passes it off to some other routine. The new caller asks for http:query:stuff, and the following happens:
Object C: Is there a value at http:query:stuff? Yes
Object C: return NULL (which is the value)
And now the new caller is explicilty lied to about the non-existence of http:query:stuff. The ability to override values in that way (with NULL or an actual value) is an explicit feature and design decision of the context system; arguably it's one of the key reasons we're going to all this trouble. :-)
Now, for this new caller, here are all (OK, most) of the possible operations we could, possibly, consider supporting. In practice we will not, nor should we, implement all of them. The question is which ones do we want, and why.
<?php
// 1) Retrieve a value, we don't care where it comes from.
$c->getValueWhereverItComesFrom('http:query:stuff');
// 2) Returns TRUE if the value will be something other than NULL,
// regardless of whether that's from an explicit set or a handler
// at any level.
$c->isThisValueGoingToBeNotNull('http:query:stuff');
// 3) This would return TRUE if and only if a handler responded with
// an explicit value or an explicit \Context\OffsetIsNull object
// (which means "it's null, don't keep checking parents").
$c->isThereAnExplicitResponse('http:query:stuff');
// 4) This would return TRUE iff there is a handler bound, at any level,
// for http:query:stuff. It would return FALSE for a handler bound to
// http:query.
$c->isThereAHandlerBoundFor('http:query:stuff');
// 5) This would return TRUE if there is a handler bound, at any level,
// to http:query:stuff, http:query, or http.
$c->isThereAHandlerResponsibleFor('http:query:stuff');
// 6) Returns TRUE iff a value has been explicitly set for http:query:stuff,
// not if there's a handler bound there.
$c->isThereAValueBoundFor('http:query:stuff');
// 7) Done before a context object is locked, this explicitly sets a value
// for a context key.
$c->setExplicitValue('http:query:stuff', 'things');
// 8) Done before a context object is locked, this hooks up a
// handler responsible for a context key but does not actually
// generate anything yet.
$c->setHandler('http:query', 'SomeClassName', $some_config_for_the_handler);
?>As a curious side note, Mark Sonnabaum passed along an article talking about the evils of NULL and ways of avoiding it. It's written for Ruby but the concepts are universally applicable. That in turn begs the question of whether we should be banning NULL entirely and doing like variable_get(), and requiring a default value to be passed in. While that would eliminate the NULL, would that cause more problems than it solves? Should we do something else to avoid NULLs, which are generally ugly and break code with fatal errors?
<editorializing>
My own position at the moment, if I may editorialize a bit, is that our primary interest is operation 1. That is the main API that we expect callers to use. We obviously need to support operations 7 and 8, and prefer the use of 8. Operation 2 is mostly syntactic sugar, allowing you do to this:
<?php
if ($c->isThisValueGoingToBeNotNull('http:query:stuff')) {
$val = $c->getValueWhereverItComesFrom('http:query;stuff');
// Do something with $val
}
?>instead of:
<?php
if ($val = $c->getValueWhereverItComesFrom('http:query:stuff') && $val !== NULL) {
// Do something with $val
}
?>I don't mind having that operation, but I don't see it as critical.
Operations 4, 5, and 6 I believe we actively do not want. One of the design goals of the context system is that a caller should not know or care where its context information comes from, merely that it can get it. That allows us the freedom to change where it comes from without the caller knowing, caring, or needing a code change. Whether a value came from the actual HTTP request or from a unit test setup routine or from a parent block that is setting up panels-style relationships is not someething the caller should have to deal with.
For the same reason, I do not believe we want operation 3. Operation 3 allows a caller to detect if it's being lied to. By design, we don't want that.
EclipseGc mentioned in IRC that such information could be useful for debugging. I would argue that the availability of such information would make debugging more difficult. If you are debugging a routine that is taking context, you want to be able to mock the context to control what gets sent to it in a unit test and not worry about a Heisenbug. If you really need to check it in-place, that's what dpm() is for.
We do not want to support every possible operation. We want a robust API that behaves the way we want it to.
</editorializing>
As a side note, we ended up with this discussion in part because of ArrayAccess::offsetExists(), which, depending on how you want to look at it, could be taken to mean operation 2, operation 3, operation 4, or operation 5, and it's very confusing which one people are talking about. (That's why I explicitly left it out of the list above.) Most people seem to think it would mean either operation 2 (which requires calling operation 1 internally) or operation 5 (which would skip explicit-set values, maybe?). Given that, I am starting to lean toward removing it and avoiding confusion in the future, even if we could agree on which operation it means.
(Incidentally, aside from the impact on ArrayAccess I do not consider this question a blocker for further work, as most of the stuff coming up won't run into this problem, I believe.)
Discuss. :-)

Comments
How to set value without 7
How would we set the value of the contextual thing that is referenced by the string 'http:query:stuff' if we didn't have 7) as a possible function?
In this case it sounds like the context could originate from url based parameters. But that won't always be the case, right?
If these questions expose my fundamental lack of understanding of what we're discussing can you point me to the documentation that can bring me up to steam?
Software Engineer @ The Nerdery
Correct
Yes, explicitly setting a context value is an operation I believe we need to support, which is why I consider operations 7 and 8 necessary. It's 3-6 that are the question. :-)
Maybe this was already
Maybe this was already considered and thrown out...
But why not use a seperate mechanism to denote "no value" and return NULL only as a real value. The two prime candidates for a seperate mechanism in my mind would be:
I haven't been around for the earlier parts of this discussion, so I apologize if there is some obvious reason for not doing this!
Regards,
David.
Operations 4, 5, and 6 I
May I quote this from IRC:
(pounard) Crell> what I'm saying is we want 4, 5 and 6(pounard) 4 and 5 are redundant by the way
(Crell) Right, and I believe we actively do not. :-) That's why I opened it for discussion.
(pounard) and 6 tells if a handler is set, but do not tell if a handler is not set
(Crell) No, they're slightly different.
(pounard) Crell> you do not, but external developers may
My whole point is we cannot predict all usages, and developing at least the 6 which tells that the container key exists, even if there is no value (which makes the name being wrong). This is the
array_key_exists()equivalent, which is, IMHO, a really valuable information (and believe me, really easy to implement).Understand by container that "key exists, but the handler tells he has "no value" or "no value" (as in one word null) has been explicitely set here".
If it was only me, I'd go the for the total API:
<?php
namespace Drupal\Context;
use Drupal\Context\Handler\ContextHandlerInterface;
interface ContextInterface {
// Existing methods...
/<strong>
* @return bool
*/
public function hasHandlerForKey($contextKey);
/</strong>
* array_key_exists() alias.
*
* @return bool
*/
public function keyExistsAt($contextKey); // Including NULL
/<strong>
* @return mixed
*/
public function getValueAt($contextKey);
/</strong>
* isset() alias.
*
* @return bool
*/
public function hasValueAt($contextKey); // Excluding NULL
/**
* @return ContextHandlerInterface
*/
public function getHandlerAt($contextKey);
// And even more..
}
?>
Developers are adults, and these features may allow, for example, a module to set its own handler only no handler has already been set, or if it want to replace a specific handler if set but not another one it trust.
EDIT: Sorry for the second mail, I fixed english typo because I'm not a native english speaker: please tell to g.d.o administrator you cannot stand this mailing feature anymore if it makes you grunt, not me. AGAIN! Ahah. And again (added a new paragraph)... Sorry again.
Pierre.
Which key?
Remember that a context object could have a value that is set explicitly on a key, or come from a handler, or come from a handler on a prefix fragment of the full key. (http:query has a handler that response for http:query:foo, for instance.) So what does "array_key_exists()" even mean here? There's at least three things it could mean.
Furthermore, we do not provide a mechanism by which we "the value of that key is 5, and that came from an explicit key on the 4th context object down". A caller does not know if an actual value came back from a handler, a handler with parameters, from an explicit set, or how many layers down it comes from. That is by design. We actively do not want callers to be concern themselves with that. Good ol' dependency injection logic. I don't see why NULL is, or should be, any different.
Supporting "where did this NULL come from" is itself 3-4 operations, and if we do that then we would naturally also do "where did this value come from" which is another bunch of operations. All of which simply complicate the interface and architecture and undermine the basic goal: Providing injected, mockable contextual information to an object so that we can change that object's environment without changing its code.
So what does
That's why we could diversify the API as I propose. We can do it easily I don't see any blocking argument in favor or NOT doing it.
My proposal does not gives us that either, I proposed a much more simple API that let the caller know, if it wants to know, if there's a handler registered At the exact given key, or if there's a handler For the given key (can be less specialized) or if a value is already set At this position.
Even if you don't see any use case now, we have the power to implement it. I basically think that this API is going to be really central in Drupal 8, and giving such as powerful API with only 2 functions is a shame: it will definitely make some third-party/contrib developers angry because the API won't allow them to use it as they want.
Imagine I could use context for other stuff that Drupal runtime, I could use it my own way for heavy migration scripts: it totally makes sense. If we provide such tool, with a really complete API people will be willing to use it for such things, if we don't people will be angry and will keep redevelopping the same feature again and again.
Honnestly, stop thinking this is because you don't see any usefulness in it that it wont. You cannot guess what will do developers with your API. The more complete it will be the more people will be happy to use it. If you have a real good context handling (I think the one actually is not bad) but absolutely no flexibility in it, it will frustrate a lot of people. Don't think of the only use case you want to fulfill, think of all the use case you cannot predict that crazy good developers could need in the future.
It's not the basic use case, and most people won't probably use it (maybe not core): that's operations I don't fear. Doesn't mean nobody will never have to use it. I basically do not often use the LIKE operation in SQL, because it's really slow, nevertheless, it happens to be really useful sometime, which makes me use it carefully on a per-needed basis, so I won't impact the rest of my code. This is how I see these operations to be used.
Further this is not even a good example because 4 functions calls that are highly likely to return a statically cached result does not cost anything. 1000 functions calls are probably a lot faster than a single cache fetch, pure pragmatic, precompile bynary code run without any IO or stuff that really makes what bottlenecks are, no heavy computing, etc.
For such a central API, we have no excuses at all not making it complete. A non complete API means that people will end up, whatever you think, whatever you designed, by bypassing it and reinventing the wheel again and again: if you really do not want to have a complete API, then this API is useless for developers.
EDIT: Plus, it's easy to realize, I re-did it, the patch, same methodology, from scratch like 3 or 4 times, each time a bit different but each time giving the exact same set of features. Did it on multiple branches, in multiple state of the WSCCI main branch, and always succeeded in making tests pass. Stop saying it's complex, it's NOT.
Once again, I'm reintroducing pieces of this proposal in handler inheritance patch I was working on, also added some bits in the parenting handling proposal I made, also it clears a lot interfaces to introduce some of these methods: it makes it easier to understand the code, lots of atomic methods but complex stuff are more readable this way. Each bit of complexity is encapsulated in its own block. I really, once again, don't see why we should not do it.
Pierre.
Sample code
I did a branch that implements all of what I said upper, without complexifying the internal algorithms. You can see by yourself in the WSCCI sanbox git, check for the do-179154-full-api branch (latest commit).
EDIT: Of course, all actual unit tests pass (didn't add new though, should have).
Pierre.
Is NULL meant to be allocated
Is NULL meant to be allocated into memory? NULL pointers don't allocate anything into memory, so that convention should apply for high level abstract languages. But if it has any meaning at all for the php language due to the code base, then it is sometimes like accessing a boolean value type that equals 0. Responses to NULL by callers should not progress further in the function nor allocate memory, but rather go back and initialize, whatever it needs to initialize. That's how it should be dealt with imo.
My idea is to borrow a
My idea is to borrow a concept from a place where this problem is solved elegantly. Namely Haskell. I am thinking about the Maybe typeclass.
The implementation is not too hard in PHP:
<?php
interface Maybe {
public function __invoke();
}
class Just implements Maybe {
protected $var;
public function __construct($var) {
$this->var = $var;
}
public function __invoke() {
return $var;
}
}
class Nothing implements Maybe {
public function __invoke() {
return NULL;
}
}
?>
It is not too radical shift, while it is quite powerful. If we don't care about if the value is actually exists or not, just the value, we can wrap it with call_user_func(). If we care, we can get the information with instanceof.
Example:
<?php/**
* Getter for foo.
*
* @param int $id
* Identifier of a foo.
*
* @return Maybe
* Returns Just $foo if foo exists, Nothing otherwise.
*/
function getFoo($id) {
$foos = get_all_foos();
return isset($foos[$id]) ? new Just($foos[$id]) : new Nothing();
}
?>
<?php
$foo = call_user_func(getFoo($id)); // we don't care about the existence
$foo2 = getFoo($id2);
if ($foo2 instanceof Just) {
print $foo2;
}
elseif ($foo2 instanceof Nothing) {
print t('Foo not found.');
}
?>
How is that, in practice, any
How is that, in practice, any different from simply returning NULL?
Huh?
I have to admit, I have absolutely no idea what you just said or what it has to do with whether or not we allow callers to tell where a NULL came from.
Sorry, my post is a bit
Sorry, my post is a bit unclear and has some typos, it was late night here.
This method could be good for handling NULL values. NULL might be a value, which comes from a database record's field. It is also possible that later, this method can substitute FALSE in a lot of DAPI functions (*_load() for example).
Nothing is explicitly saying that the value does not exists.
Tracking NULL: with this concept, a class can be made which has information about its origin. Like this:
<?php
define('DRUPAL_CONTEXT_SOURCE_VALUE', 0);
define('DRUPAL_CONTEXT_SOURCE_HANDLER', 1);
interface ContextMaybe extends Maybe {
public function getParent();
public function getSource();
public function getLevel();
}
class ContextJust extends Just implements ContextJust {
protected $parent; // context object
protected $source; // source: DRUPAL_CONTEXT_SOURCE_VALUE or DRUPAL_CONTEXT_SOURCE_HANDLER
protected $level; // decision level; it can be http:query:foo or http:query or http for example
public function __construct($val, $source, $parent, $level) {
parent::__construct($val);
$this->source = $source;
$this->parent = $parent;
$this->level = $level;
}
public function getParent() { return $this->parent; }
public function getSource() { return $this->source; }
public function getLevel() { return $this->level; }
}
// The implementation of ContextNothing is similar.
?>
Considering the examples in
Considering the examples in Crell's original post...
Object A: Call the handler, get back "things". Save that value to http:query:stuff, then return it.Object B: Cache "things" to http:query:stuff here, too. Return it to the caller.
Does this mean that A returns "things" to the Caller, or Object B... and if the latter, what does B do if A returns NULL? I assume it returns NULL because it does not know what the value should be – NULL in PHP meaning an unknown value, which is not set and is empty.
Which makes this example seem incorrect to me...
Object C: Is there a value at http:query:stuff? YesObject C: return NULL (which is the value)
because what we are saying here is that
Object C:http:query:stuffis NULL, hence empty and not set (i.e. it has no value). I dont see an easy way of checking that a NULL value exists and returning it, without explicitly breaking the PHP NULL convention (and hence expected behaviours, see reference 1).I see three possible solutions to this, one of which is similar to Yorirou's, above...
1) Use a constant, e.g CONTEXT_NULL, which contains a value never likely to be set by a caller (similar to solution above, but works with strings).
2) Set NULL explicitly with constant() inside the Context object (probably not recommended) (see reference 2).
3) Use a constant to mark that a value should be skipped, e.g. CONTEXT_IGNORE.
Item (1) would turn the example into something like...
Now, another context object C, is added to the stack. The caller explicitly assigns CONTEXT_NULL for http:query:stuff, then passes it off to some other routine. The new caller asks for http:query:stuff, and the following happens:
Object C: Is there a value at http:query:stuff? Yes, CONTEXT_NULL.Object C: return CONTEXT_NULL (which is the value)
for which the caller could do something like...
if ($context[http:query:stuff] == CONTEXT_NULL) {...To truly set the value to NULL should trigger traversal back to
Object C:http:query.What might be difficult is working out what the CONTEXT_ constants might hold, possibly a namespaced value like "drupal_context_null", or a UUID.
References
(1) http://php.net/manual/en/types.comparisons.php
(2) http://stackoverflow.com/questions/137487/null-vs-false-vs-0
Alternatively, the Object
Alternatively, the Object could also return NULL when it found CONTEXT_NULL, which completes the circle.
Alternatively you can
Alternatively you can implement a method like
hasValueAt($position)(or name ithasContainerAtorkeyExistsor anything else, doesn't matter as long at is stay explicit) that superseeds the NULL and allow the caller, if it cares, to check if the NULL has a container returning it, or if the NULL is just a void that means: really nothing ever registered here.<?php
// I don't care:
if ($value = $context->getValueAt('some:key')) {
// Do something.
} else {
// No value, I probably don't care or use a default then.
}
// I care in the meaning of NULL (like checking the presence of a module):
$key = 'some:key';
if ($context->hasValueAt($key)) {
if ($value = $context ->getValueAt($key)) {
// Do something.
} else {
// Do other thing with my value, but notice I could do the same thing instead just upper removing the if.
}
} else {
// OMG! No container here! WTF am I gonna do?!
}
?>
The beauty here is you can use both! Just depend on if you care or not, but at least, both people, the one who cares, and the one who doesn't care will be happy forever after together (and probably NOT have children together since they are both developers from the opposite side of the world).
The problem of using a constant is what value are you gonna put in? If you set "2" this means no node can never have the nid "2", if you put NULL this means you will confuse it with the NULL constant, and if you put FALSE it means that no value ever can be FALSE, you just deport the problem elsewhere.
EDIT: Sorry, the PHP code syntax highlighter is definitely bugguy and replaces my /* by / // WRONG! DUDE!
Pierre.
The problem of using a
Absolutely, which is why I suggested a namespaced string, its a bit of a hack, but not unreasonable.
The problem I can see with your $context->getValueAt() method, above, is what happens inside the object, not what gets returned. According to our current logic, a value which is NULL should be skipped and traversed to the next value, meaning no handler would ever return NULL unless it was NEVER set at ANY LAYER.
Does this mean that A returns
It means A returns things to B, and B returns it to the caller. If A returns NULL, B returns that to the caller. It just bubbles it up.
Additionally, Drupal has
Additionally, Drupal has often in the past used FALSE to indicate that zero values were found. This seems a reasonable convention, and gets around the "nil" problem in the article by Joe Ferris. Context should return FALSE when something is explicitly not set, as opposed to NULL, which is for when it is unknown.
Or not, a boolean value is
Or not, a boolean value is something that totally can has a business meaning.
Pierre.
A boolean can have a business
A boolean can have a business meaning, but in most places currently it means either "There was nothing" or "There was something, but it was nothing", i.e. FALSE. It almost never means "I don't know if there was anything", which is what NULL means.
We are not just talking about NULL here, we are also talking about differentiating between NULL, 0 and FALSE, so...
Technically the last one is a bit of a fudge, as NULL is equivalent to an unset key.
For the sake of discussion, I
For the sake of discussion, I have in mind an example shown at simplecloud:
<?php
interface Zend_Cloud_DocumentService_Adapter
{
const HTTP_ADAPTER = 'http_adapter';
After taking a look at it, I sort of agree that a caller should not care about the information it gets for forward facing functions. But in order for the backend to properly run, additional functions makes lives easier down the road, when they get information from the caller. If the caller doesn't have a way of getting certain types of information from whichever language, the backend can run into problems.
For something like $options in the above example, NULL has less business meaning than zero later on. If one takes your convention, then the above example in this response should return FALSE instead of NULL, but for something like $options, it looks as though it is meant to hold a string type, so I would prefer that it holds NULL, instead of false, so as to write the right type of handler code, instead of getting boolean checks code in the way of backend plumbing (coding).
I think the $c->isThereAnExplicitResponse('http:query:stuff'); isn't a bad idea for a caller function. In terms of regex matching, I would assume that the #3 function can have fast uses. It should be fundamental to have atomic functions, but having more functions does make a developers job easier at times. I would find it useful for health/sanity checks, since the status of the network isn't always a given. Perhaps the 3rd party code can use it for a keepalive connection or scheduler, since it seems to be able to provide some type of enumeration of the status on the other side.
"A context key which is numerically ZERO, should return 0." I agree, but shooting for semantic correctness isn't always ideal.
Also, I do not believe that NULL is "unknown" as you've stated earlier, and it sounds like your definition is like dealing with the inverse sets of cardinal numbers, while NULL for me - in philosophical terms - cannot be known (it can't interface with anything - not even itself), not set, not initialized, etc.
My point is, that the functions provided shouldn't limit itself in providing only a mirror of incoming traffic. Within a MVC context, a caller will probably spend most of it's time coordinating with controllers, but in order to provide appropriate views, a caller is a type of handler in a way too.
I'm also thinking about security but I haven't looked into the rest of the functions and the project yet.
Ok, after chating with Crell
Ok, after chating with Crell over IRC, I have to agree about one point:
I can understand why you want to rule this out of the interface itself, this make sense that people manipulating a raw interface should not care about what is a handler (and you can change the internals later). As the consumer, knowing handlers is not really that important.
But I don't get why I cannot let these methods exist as public in the Context default class: having a fully featured context seems quite a pleasant idea to me. If you choose to go all the way with the handlers, this is not something you will be able to remove easily later, I explain:
Now, as the creator (and not the consumer) , I will be manipulating handlers anyway, at least in order to set them and create my override. If I create my own context: I basically manage handlers all the way whatever happens, why couldn't access them as a public API?
I guess that the consumer really does not need to know what the handlers are and what they exists for (and that's where I agree now): if we change the handlers internal logic, we won't have to fix this code (depedency injection, etc..), but for the creator logic: all code that add new handlers or push handlers into contextes will need to be fixed anyway. No more harm done that it already has been done just by creating the handler abstraction. So really don't get why I couldn't have those methods.
No even if at least I agree about this point (and yes Crell is right about the interface). This doesn't mean I won't continue to argue in favor of a hasValueAt() method, that tells YES even if it's a NULL (a handler or a pragmatically set one, doesn't really matter, but a consciensiously set NULL as value, meaning: someone handles the NULL, and the value is no value).
EDIT: One reason I originally defend having the handler in the interface is for the handler inheritance problem, that would some some of the internal problems of the objects.
I have a Context A, and a Context B (A is parent of B):
1. In B, I would fetch a value that is handled by A,
2. This value is dependent of some values in B itself (it's a specific handler),
3. Handler should not be dependent on context conceptually (circular dependency),
4. In order to inject B into the A handler when I fetch the value, I need to get the A handler in B,
5. I need a Context::getHandlerAt($key) on A in order to fetch it.
Now, same problem, except A is not Context, but BusinessContext that implements ContextInterface
All the same until 5,
What do I do then? getHandler() is not part of the interface, I'm blocked.
How can we solve this kind of problems if handler is not meant to be part of the overall design but only an internal helper? I guess we could protect the method in the interface, but:
Assume this being test.php file:
<?phpinterface World {
protected function sayHello();
}
?>
Then, this happens:
Mon Oct 03, 19:49] $ php -f test.phpPHP Fatal error: Access type for interface method World::sayHello() must be omitted in /home/pounard/test.php on line 4
So how are we going to solve this problem then if handlers are only a Context class internal helper while the ContextInterface can be implemented by pretty much everyone and injected into the chain at any time. If you choose that another specialisation of Context never can be injected in the chain, how could I do a specific handlerless implementation that needs to implement a really specific business logic?
Pierre.
Now, as the creator (and not
I don't follow. If you are adding a layer to the context stack, you use setExplicitValue() or setHandler() methods. Problem solved.
TBH, I don't expect anyone to actually implement an alternate version of \Drupal\Context\Context. I setup an interface for it originally more out of habit than anything else. :-) Even if someone did, the only place that's an issue is the stacking logic (which is a separate matter from this thread).
I still don't see a use case for that which would not at the same time violate conceptual encapsulation. Can you think of any? (And please use the method names or numbers in the OP so that we can keep track of what operations we're talking about. As above, "has a value" could come from at least 2 places.)
OK, context dependency is a valid issue. We cannot not have dependent contexts. (Current node by default depends on current HTTP URI, so that's a dependency right there.) When traversing up the tree we probably need to have a way to pass up the context that should be passed to the handler, so that it always starts looking at the upper-most context object. I've actually got an idea for how to do that, but it doesn't affect the NULL question.
I don't follow. If you are
Yes, but handlers already are in the interface signature then, so are handlers are being part of the contract: consistency problem solved so it wouldn't hurt to have some other handlers manipulation logic being public as well.
I guess it's still something possible, and why not actually, it would make sense to embed business in some very specific context scope.
This doesn't violate the encapsulation principle, this only allows to use NULL as a valid value.
Pierre.