* Affected components: None immediately, but PageIdentity is designed to replace many uses of the WikiPage and Title classes. So eventually: much of MediaWiki.
* Engineer(s) or team for initial implementation: @daniel / #core_platform_team
* Code steward: #core_platform_team
### Motivation
Create a light weight replacement for WikiPage and Title to be used to represent editable wiki pages.
The Title class is not only large and has many dependencies, it's also conceptually overburdened: it is used to represent link targets on the one hand, and editable pages on the other. It's also an "active record" that uses global state to interact with the storage backend, which is something we want to move away from.
Similarly, the WikiPage class contains database logic and business logic, and relies on global state.
A light-weight value object to represent wiki pages is generally useful for decoupling, e.g. {T208764}, and to for serialized representation of deferred events, see e.g. {T212482} .
##### Requirements
* PageIdentity must make available to following: the page's internal ID, the namespace number, the title (db key), and the ID of the wiki it belongs to, for cross-wiki operations.
* PageIdentity (or a subclass or implementation) should be newable
* PageIdentity (or a subclass or implementation) should be serializable
* PageIdentity (or a subclass or implementation) should be a immutable pure value object
-------
### Exploration
One major aspect of the design of PageIdentity is interoperability with and transition from the Title class.
##### Considerations
* To easy transition, it would be useful if Title could implement PageIdentity. This allows parameters of existing methods to be narrows from Title to PageIdentity.
* To easy transition, it would be useful if WikiPage could implement PageIdentity. This allows parameters of existing methods to be narrows from WikiPage to PageIdentity.
* It would be desirable of any instance of PageIdentity was guaranteed to represent a "real" page (not a special page, not an interwiki link, not a section). However, this desire conflicts with the wish to have Title implement PageIdentity.
* PageIdentity and LinkTarget are distinct. Not all LinkTargets correspond to a PageIdentity (e.g. SpecialPages, interwiki linkls, section links, etc). Every PageIdentity has a corresponding LinkTarget, but it may not be trivial to determin if the PageIdentity belongs to another wiki.
##### Proposal
Make PageIdentity an interface and have Title implement that interface. Provide a pure value implementation, PageIdentityValue, as an alternative.
Title would again have a static castFromPageIdentity() method that would instantiate a Title from a PageIdentity, which would return the object unchanged if that object already was a Title instance.
We have been successfully using this approach with UserIdentity and LinkTarget.
The signature and semantics of the PageIdentity interface will have to make some concessions to be compatible with Title. In particular, since a Title object can represent things that are not editable wiki pages but merely link targets (Special pages, interwiki links, relative section links), PageIdentity will have to allow for such "improper" instances, and will need to offer a method for calling code to check whether it's a "proper" PageIdentity.
To provide a patch towards providing stronger guarantees, provide an additional interface, ProperPageIdentity, that represents the stricter semantics that PageIdentity should eventually represent (specifically, that it is always a page that exists or can be created). Ideally, ProperPageIdentity will become an alias for PageIdentity in the future.
Draft:
```lang=php
interface PageIdentity {
function getId(): int; // see Title::getArticleID(). Throws if it's not a "proper" page!
function canExist(): bool; // see Title::canExist. False if not a proper page. Taint, should eventually go away.
function exists(): bool; // see Title::exists. False if not a proper page.
function getWikiId(): string; // see RevisionRecord::getWikiId()
function getNamespace(): int; // see Title::getNamespace
function getDBkey(): string; // see Title::getDBkey
}
interface ProperPageIdentity extends PageIdentity {
function getId(): int; // see Title::getArticleID(). Never throws.
function canExist(): bool; // Always true. Should eventually go away.
}
class PageIdentityValue implements ProperPageIdentity {
...
}
class Title implements PageIdentity {
...
public static function castFromPageIdentity( ?PageIdentity $pageIdentity ): ?Title;
public function getWikiId(); // always false, since Title only supports the local wiki
public function getId(); //like getArticleID(), but throws if canExist() returns false.
public function asPageIdentity(): ProperPageIdentity; // throws if not a proper page
}
class WikiPage implements PageIdentity {
...
function canExist(): bool; // see Title::canExist. Always true (needs fixing of some edge cases)
function getWikiId(): string; // see RevisionRecord::getWikiId()
function getNamespace(): int; // see Title::getNamespace
function getDBkey(): string; // see Title::getDBkey
}
class TitleValue implements LinkTarget {
public static function newFromPage( PageIdentity $page ): TitleValue; // throws if not local
}
```
##### PageIdentity and PageRecord
With the proposal of PageStore (T195069) also comes the concept of a PageRecord object, representing page level meta data such as the page's latest revision, touch date, etc. PageRecord should by a subtype of PageIdentity (extending interface of implementing class).
##### Bike shed
* What terminology should be used to represent the idea that a PageIdentity refers to a "real" page, rather than a "special" page or foreign link target? "article" is ambiguous...
* What terminology should be used to identify PageIdentities that identify pages that belong to a different wiki, but one to which we still have direct database access via PageStore and RevisionStore, know the namespaces, etc? How does that relate to isExternal() and getInterwiki() defined in LinkTarget?