Skip to content

Commit

Permalink
chore: minor changes and improvements (#2)
Browse files Browse the repository at this point in the history
Adds a couple of more functions and changes behaviour of fallback in a
non breaking way.

Also, it completes the documentation. This should be the last release in
a long time.
  • Loading branch information
mnavarrocarter authored Oct 8, 2022
1 parent 85f058c commit 20d895e
Show file tree
Hide file tree
Showing 8 changed files with 591 additions and 20 deletions.
72 changes: 66 additions & 6 deletions .castor/docs/best-practices.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,74 @@
Best Practices
==============

## Pass `Context` as the first argument of the function or method
The `Context` api is definitely a new pattern and approach to solve the context passing problem in PHP.
Whenever a new pattern is introduced, is always important to define some best practices for its good use.

## Don't overuse `Context\with_value` for related values
These are some of what we consider to be those best practices.

## Use Enums as Keys
## Custom functions SHOULD be defined to work with `Context`

## Do not store `Context` inside objects
Context is all about composition, and you should also compose the base context functions to provide a nicer api
to store or retrieve context values, like we did in the logger example. Of course, you could also rely on
static methods, but in my personal opinion, functions are nicer.

## Avoid immutable values inside `Context`
You can even go as far as to create your own `Context` implementation that composes a `Context`. We do that for some
of our libraries. But we even discourage that. The thinner you can keep `Context` api surface, the better.

## Do not store services inside the `Context`
## `Context` SHOULD be the first argument of the function or method

Whenever a function or method call accepts a `Context`, this should be the first argument of the function, and
it should be named `$ctx`. We know some people hate abbreviations and prefer more explicit names. I agree with that
suggestion almost in every situation, but in this case the abbreviation does not yield any sort of confusion and
is pretty universal and easily understood.

It also has the benefit that it is short, and since `Context` is there to be added to your public api, we want
to minimize as much as possible the space impact that has in your method's argument list.

## Enums SHOULD be used as keys rather than strings

When calling `Context\with_value()` prefer enums as keys. They are lightweight, offer autocompletion, and they cannot
collide like strings could. In the case that your application still does not support PHP 8.1 and above, you MUST use
string keys with a vendor namespace.

## `Context` SHOULD NOT be stored inside other data structures

Always explicitly pass `Context`. Do not store it inside objects or arrays, unless is explicitly necessary. For
instance, if you are using the `Context` api in PSR-7 applications, it is very likely your `Context` instance
will be stored in the Request attributes (which is implemented by an array). This is acceptable, but for a better
HTTP layer supporting context natively, we recommend `castor/http`.

## `Context\with_value` SHOULD NOT be overused

Because of its particular implementation, every time you add a value to a `Context`, you increase the potential call
stack size to reach the value by 1. Although the performance impact of this is negligent, is still slower than fetching
a value directly from a map, for instance.

So, bottom line, don't overuse `Context\with_value`. This means that if you have to store related values in
`Context`, store a data structure instead and not each value individually.

Again, the performance impact of not doing this is negligible, so measure and make decisions based on that.

> In a potential new major version, we are considering swapping the `Context\with_value()` implementation by using a
[`DS\Map` if the extension is available][https://www.php.net/manual/en/class.ds-map.php] to avoid the performance
penalty.

## `Context` SHOULD NOT contain immutable values

A `Context` implementation is already immutable, and it's lifetime does not go outside the request. This means it is
safe for manipulation and free of unexpected side effects.

As long as `Context` holds values derived from the request, whose lifetime will also die with it, then it is safe to
store mutable values in it. If you store immutable values and decide that a new reference of that value needs to be
passed down the call stack it means the value should have never been immutable in the first place. You'll have to call
`Context\with_value` again and "override" that value.

## Services SHOULD NOT be stored inside `Context`

We greatly discourage storing services inside `Context`. It is not a good idea to mix request-scoped values with
application scoped-values like services. Always prefer dependency injection instead of passing services down the
context.

There could be some use cases when this could make sense. For instance, if you need to pass an event loop instance
down the call stack to reach some method and make it work asynchronously. I would say that is part of the execution
context, although an event loop can be classified as a service.
9 changes: 8 additions & 1 deletion .castor/docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,11 @@ let me use any type as key. Arrays only support integers and strings.
## 2. Should I use it in my domain?

Yes! It is designed for this very purpose. Once you add the `Context` interface as a type hint of a repository
method or some other function, a world of possibilities are opened in terms of evolvavility and extensibility.
method or some other function, a world of possibilities are opened in terms of evolvavility and extensibility.

## 3. Will this package ever have breaking changes?

No. Context promises a stable api since is really one of the most important building blocks of our libraries.

However, we are considering expanding the api surface for a possible `v2` version once we have implemented async
libraries, and we decide we need cancellation signals, similar to what Golang context api has at the minute.
Loading

0 comments on commit 20d895e

Please sign in to comment.