Topic on Talk:Deprecation policy

Summary by DKinzler (WMF)

This proposal has evolved into phab:T193613 and the Stable_Interface_Policy draft.

DKinzler (WMF) (talkcontribs)

Service objects are not intended to be instantiated directly. As such, their constructor signatures should not be considered part of the public stable interface by this policy. Ideally, extensions (and any application logic) only ever directly instantiates plain value objects. I propose to amend the deprecation policy to explicitly state that only constructor signatures of plain value objects are considered stable. Changes to the constructor signature of service objects and such do not have to follow the deprecation policy.

Krinkle (talkcontribs)

I agree they should be considered private. But, I think the policy already provides for this use case, with @internal. The constructors can be marked internal, right?

DKinzler (WMF) (talkcontribs)

We could do that, yes. But I'd prefer them to be considered internal per default. There are really just a few things that should be considered "newable" by application logic and extensions. I'd like to treat this the same as sub-classing: if you are directly calling the constructor that is not explicitly declared to be stable, you are on your own.

Simetrical (talkcontribs)

Service classes might be subclassed, though, like DefaultPreferencesFactory -> GlobalPreferencesFactory in extensions/GlobalPreferences/. Then they'll have to invoke the constructor, right?

Why don't we make the constructors not callable directly at all? PHP doesn't officially support friend functions, but you can do it by passing around callbacks. Make service class constructors private, then give each service class a method

  public static function provideConstructor( MediaWikiServices $services ) : callable {
    $services->takeConstructor( self::class, function ( ...$args ) {
      return new self( ...$args );
    };
  }

and then let MediaWikiServices keep an array of constructors it can call but no one else has access to. This might be, um, slightly hacky, but there's nothing else that's realistically going to prevent everyone in the codebase (including core!) from directly instantiating all these classes.

DKinzler (WMF) (talkcontribs)

We already consider subclassing unsupported, unless the class or interface explicitly documents that it can be subclassed/implemented by extensions. It's a bit buried, and doesn't talk about constructors, but it's there:

some classes expect to be subclassed in extensions; in those cases protected functions also are included in the API. These classes should have a note in their documentation comment that they expect subclassing. If no note is present, it SHOULD be assumed that the class is not expected to be subclassed.

Perhaps we should have a section that explicitly discusses subclassing and constructors.


That being said: if DefaultPreferencesFactory expects to be subclassed, its constructor should be considered a stable public interface which is subject to the deprecation policy.