Wim Leers
@wimleers
wimleers.com
Principal Software Engineer, Drupal Acceleration Team ("OCTO"),
Things I learned so I know slightly better what I do not know π€
can be used for anything!
β¬
All code must have an API to be generic & overridable
but not enough time to carefully design every API
β¬
Overengineered
β¦ yet underengineered!
β¬
BC = nightmare. Let's get better.
We have gotten better! π₯³
Promise of updating without things breakingπ€©
Does this mean I can't ever fix mistakes from the past?π©
Does this mean I can never do this cool thing?π₯Ί
I can't possibly maintain this spaghetti code of backwards compatibility layers!π€―
d.o/core/d8-bc-policy: @api
vs @internal
But in Drupal coreβ¦
@api
@internal
99% "undocumented"
β 99% considered API
Two years later:
@internal
occurrencesFor public APIs, the deprecation process is a is a requirement; for internal APIs, we provide BC and deprecation where possible, but might make breaking changes in situations where BC is not possible or has negative consequences.
@deprecated
404
Two years later:
Current best practice?
/**
* Sets a message to display to the user.
β¦
* @deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0.
* Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead.
*/
function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) {
@trigger_error('drupal_set_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead. See https://www.drupal.org/node/2774931', E_USER_DEPRECATED);
$messenger = \Drupal::messenger();
if (isset($message)) {
$messenger->addMessage($message, $type, $repeat);
}
return $messenger->all();
}
run_tests.phpunit:
types: 'PHPUnit-Unit'
testgroups: '--all'
suppress-deprecations: false # π₯³
halt-on-fail: false
if (\Drupal::hasService('messenger')) {
\Drupal::messenger()->addMessage(β¦);
}
else {
drupal_set_message(β¦);
}
#3024461: Consistent deprecation messages format
β¬
Q: Why does Drupal have so many APIs?
A: Optimized for targeted overrides
β granular APIs
β many, many APIs
Drupal allows you to replace a particular leaf.
Others require replacing a branch or even a tree.
Drupal has 3 types of APIs
The API assumption
Part 1: Accidental API
Every class must have an interface
- Clients don't see any benefit in spending money on a complete re-platforming - the changes are too big to be considered just an "upgrade"
— Sally Young (@justafish) April 3, 2019
- All levels of developer are finding D8 incomprehensible because the API surface is too large, with too many options and missing/poor docs
Issue #2266809: Make QuickEditEntityFieldAccessCheck::access() use the $account that's passed in
* @param string $field_name
* The field name.
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * The user for which to check access.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
- public fn accessEditEntityField($entity, $field_name);
+ public fn accessEditEntityField($entity, $field_name, AccountInterface $account);
}
(Introduced by yours truly in #1824500: In-place editing for Fields
on Dec 21, 2012.
Suffering the consequences many years later.)
Poorly designed APIs make BC very difficult
Prefer duplication over the wrong abstraction
API when:
Little work + high complexity (granular APIs)
vs
More work + low complexity (duplication)
Part 2: orthogonality
β using Entity API === using >10 APIs!
NodeInterface $node
1. NodeInterface extends ContentEntityInterface, EntityChangedInterface, EntityOwnerInterface, RevisionLogInterface, EntityPublishedInterface
2. ContentEntityInterface extends \Traversable, FieldableEntityInterface, RevisionableInterface, TranslatableInterface
3. FieldableEntityInterface extends EntityInterface
4. EntityInterface extends AccessibleInterface, CacheableDependencyInterface, RefinableCacheableDependencyInterface
FieldableEntityInterface::get()
aka $node->get($field_name)
1. FieldItemListInterface extends ListInterface, AccessibleInterface
2. ListInterface extends TraversableTypedDataInterface, \ArrayAccess, \Countable
3. TraversableTypedDataInterface extends TypedDataInterface, \Traversable
4. TypedDataInterface
Massive composition & long inheritance chains require massive knowledge
Part 3: assumptions
X
X
X
Y
Breaks when advagg
is installed π
$request = $this->requestStack->getCurrentRequest();
$link_headers = $request->attributes->get('http2_server_push_link_headers', []);
foreach ($elements as &$element) {
+ if (!static::isLinkRelStylesheet($element)) {
+ continue;
+ }
+
// Locally served CSS files that are sent to all browsers can be pushed.
- if ($element['#tag'] === 'link' && $element['#browsers']['!IE'] === TRUE && $element['#browsers']['IE'] === TRUE && $element['#attributes']['href'][0] === '/' && $element['#attributes']['href'][1] !== '/') {
+ if (isset($element['#attributes']['href']) && static::hasRootRelativeUrl($element, 'href') && static::isUnconditional($element)) {
$link_header_value = '<' . $element['#attributes']['href'] . '>; rel=preload; as=style';
$link_headers[] = $link_header_value;
Make assumptions explicit
even better:
Test assumptions
BigPipe + Dynamic Page Cache
Try hard to not provide an API
(Then BC is kept as long as functionality works!)
Functionality first, API later
or mark@internal
first, remove later
Prefer duplication over the wrong abstraction
Test 1) critical path, 2) edge cases, 3) assumptions
(especially for APIs)
π€
Locate this session at the
DrupalCon Seattle website:https://events.drupal.org/seattle2019/schedule