Page MenuHomePhabricator

BlockTarget class
Closed, ResolvedPublic8 Estimated Story Points

Description

A block target typically starts out as a string which is passed to BlockUtils::parseBlockTarget() which returns a tuple, the first element being a UserIdentity|string|null union, and the second element being an integer type. In various places in the code, the UserIdentity|string|null union that is returned is passed by itself into a method that calls parseBlockTarget() again, despite it not being idempotent. Sometimes, the "#" prefix for autoblock IDs is re-added before passing the target to parseBlockTarget() -- this gets it closer to a correct round-trip.

I logged 10 calls to parseBlockTarget() during an API block action.

BlockUtils::parseBlockTarget() has some UI level features which don't really belong in backend code, like trimming spaces. The "#" prefix is properly a UI feature.

The union itself is awkward and the relevant code spends a fair few lines figuring out what is going on with it.

So I propose a BlockTarget class hierarchy.

Proposal:

  • Add BlockTargetFactory
  • BlockTargetFactory::newFromString() replaces BlockUtils::parseBlockTarget()
  • BlockTarget::isValid() instead of BlockUtils::validateTarget(). This is pretty lightweight and doesn't currently depend on services. If it did depend on services, the factory could just inject a validity flag, there isn't really a need to defer validity checking.
  • There will be nothing left in BlockUtils and it can be deprecated.
  • DatabaseBlockStore methods that take a union will be migrated to take a BlockTarget instead. BlockTarget will temporarily be added to the union, with string|UserIdentity being hard-deprecated.
  • BlockUser and UnblockUser constructor parameters will be migrated.
  • Add Block::getTarget().
  • Callers would benefit from the following convenience methods: toString(), toHex(), toHexRange(), getUserPage(), getUserIdentity().

I am leaning towards instanceof with many small subclasses, as an alternative to nullable accessors. Consider:

$page = $target->getUserPage();
if ( $page ) {
   ...
}

versus

if ( $target instanceof BlockTargetWithUserPage ) {
    $page = $target->getUserPage();
}

The latter has short and simple getter implementations. Static analysis on the calling code ensures the getter is safe to call. It's a conditional downcast instead of an error check. IP address accessors like toHex() would not need to be nullable in such a scheme, since the factory validates the address before creating the object.

Event Timeline

Restricted Application added a subscriber: Aklapper. · View Herald Transcript

Nothing in codesearch uses Block::TYPE_ID. Things check whether the type is TYPE_ID but nothing ever sets it to that. I will probably drop it.

For permissions and update, autoblocks are represented with TYPE_IP. For query and display, they are represented with TYPE_AUTO. It's a complication. The options are:

  • Carry the data model over unchanged. Something in the constructor's call chain will decide whether to obfuscate autoblocks. DatabaseBlockStore::newFromRow() can call BlockTargetFactory::newRawFromRow() and BlockListPager can call BlockTargetFactory::newObfuscatedFromRow().
  • Have a target class which carries both the ID and IP, and let the caller of various accessors decide whether to obfuscate it or not.

I'm leaning towards the first option.

Change #1117853 had a related patch set uploaded (by Tim Starling; author: Tim Starling):

[mediawiki/core@master] block: Add a BlockTarget class hierarchy

https://gerrit.wikimedia.org/r/1117853

Change #1118000 had a related patch set uploaded (by Tim Starling; author: Tim Starling):

[mediawiki/core@master] block: DatabaseBlock constructor caller migration

https://gerrit.wikimedia.org/r/1118000

Change #1118259 had a related patch set uploaded (by Tim Starling; author: Tim Starling):

[mediawiki/core@master] block: Deprecate UserIdentity|string block target specifications

https://gerrit.wikimedia.org/r/1118259

Change #1117853 merged by jenkins-bot:

[mediawiki/core@master] block: Add a BlockTarget class hierarchy

https://gerrit.wikimedia.org/r/1117853

Change #1118000 merged by jenkins-bot:

[mediawiki/core@master] block: DatabaseBlock constructor caller migration

https://gerrit.wikimedia.org/r/1118000

Change #1118259 merged by jenkins-bot:

[mediawiki/core@master] block: Deprecate UserIdentity|string block target specifications

https://gerrit.wikimedia.org/r/1118259

Change #1143488 had a related patch set uploaded (by D3r1ck01; author: Tim Starling):

[mediawiki/core@REL1_44] block: Deprecate UserIdentity|string block target specifications

https://gerrit.wikimedia.org/r/1143488

Change #1143488 merged by jenkins-bot:

[mediawiki/core@REL1_44] block: Deprecate UserIdentity|string block target specifications

https://gerrit.wikimedia.org/r/1143488