XOMW: Start XomwServiceContainer [#632]

staging
gnosygnu 4 years ago
parent b0e02979ac
commit 2dab6a90be

@ -1,6 +1,6 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
Copyright (C) 2012-2020 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
@ -13,22 +13,39 @@ The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.mediawiki; import gplx.*; import gplx.xowa.*;
package gplx.xowa.mediawiki;
import gplx.Bool_;
import gplx.Bry_bfr;
import gplx.Bry_bfr_;
import gplx.Char_;
import gplx.Int_;
import gplx.Object_;
import gplx.Ordered_hash;
import gplx.Ordered_hash_;
import gplx.Type_;
import gplx.core.brys.*;
// NOTE: Object-representation of PHP Array; REF.PHP: https://www.php.net/manual/en/language.types.array.php
import java.util.Iterator;
import java.util.function.Consumer;
// REF.PHP: https://www.php.net/manual/en/language.types.array.php
// Will also will have static functions but "array_" will be stripped; REF.PHP: https://www.php.net/manual/en/ref.array.php
public class XophpArray implements Bry_bfr_able {
private final Ordered_hash hash = Ordered_hash_.New();
private int nxt_idx;
public class XophpArray<T> implements Bry_bfr_able, Iterable<T> {
private final Ordered_hash hash = Ordered_hash_.New();
private int newMemberIdx;
public void Clear() {
hash.Clear();
newMemberIdx = 0;
internalPointerIndex = 0;
}
public int Len() {return hash.Len();}
public int count() {return hash.Len();}
public boolean count_bool() {return hash.Len() > 0;}
public boolean isset(String key) {return hash.Has(key);}
public boolean isset(int idx) {return idx >= 0 && idx < hash.Count();}
public boolean in_array(String v) {return Has(v);}
public static boolean is_array(Object val) {
return Type_.Eq_by_obj(val, XophpArray.class);
}
public Object end() {
int len = hash.Len();
return len == 0 ? null : ((XophpArrayItm)hash.Get_at(len - 1)).Val();
@ -54,30 +71,25 @@ public class XophpArray implements Bry_bfr_able {
}
return rv;
}
public void Clear() {
hash.Clear();
nxt_idx = 0;
}
public XophpArray Add(Object val) {
int key = nxt_idx++;
int key = newMemberIdx++;
Set(XophpArrayItm.New_int(key, val));
return this;
}
public XophpArray Add(int key, Object val) {
nxt_idx = key + 1;
newMemberIdx = key + 1;
Set(XophpArrayItm.New_int(key, val));
return this;
}
public XophpArray Add(double key, Object val) {
int key_as_int = (int)key;
nxt_idx = key_as_int + 1;
newMemberIdx = key_as_int + 1;
Set(XophpArrayItm.New_int(key_as_int, val));
return this;
}
public XophpArray Add(boolean key, Object val) {
int key_as_int = key ? 1 : 0;
nxt_idx = key_as_int + 1;
newMemberIdx = key_as_int + 1;
Set(XophpArrayItm.New_int(key_as_int, val));
return this;
}
@ -88,7 +100,7 @@ public class XophpArray implements Bry_bfr_able {
}
else {
Set(XophpArrayItm.New_int(key_as_int, val));
nxt_idx = key_as_int + 1;
newMemberIdx = key_as_int + 1;
}
return this;
}
@ -235,11 +247,32 @@ public class XophpArray implements Bry_bfr_able {
return rv;
}
public static XophpArray New(Object... vals) {
XophpArray rv = new XophpArray();
for (Object val : vals)
rv.Add(val);
return rv;
@Override
public Iterator iterator() {
return new XophpArrayIterator(hash);
}
class XophpArrayIterator implements Iterator<T> {
private final Ordered_hash hash;
private int curIdx;
private int len;
public XophpArrayIterator(Ordered_hash hash) {
this.hash = hash;
this.len = hash.Len();
}
@Override
public boolean hasNext() {
return curIdx < len;
}
@Override
public T next() {
return (T) hash.Get_at(curIdx++);
}
@Override
public void remove() {
throw new XophpRuntimeException("remove not supported");
}
}
// REF.PHP:https://www.php.net/manual/en/function.reset.php
@ -259,5 +292,14 @@ public class XophpArray implements Bry_bfr_able {
return array.internalPointerAdd(1);
}
public static final XophpArray False = null; // handles code like "if ($var === false)" where var is an Object;
public static boolean is_array(Object val) {
return Type_.Eq_by_obj(val, XophpArray.class);
}
public static final XophpArray False = null; // handles code like "if ($var === false)" where var is an Object;
public static XophpArray New(Object... vals) {
XophpArray rv = new XophpArray();
for (Object val : vals)
rv.Add(val);
return rv;
}
}

@ -119,7 +119,7 @@ public class XophpArray_ {
return rv;
}
public static XophpArray array_keys(XophpArray array) {
public static XophpArray<String> array_keys(XophpArray array) {
XophpArray rv = XophpArray.New();
int len = array.count();
for (int i = 0; i < len; i++) {

@ -1,6 +1,6 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
Copyright (C) 2012-2020 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
@ -13,7 +13,14 @@ The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.mediawiki; import gplx.*; import gplx.xowa.*;
public class XophpError extends Err { public XophpError(String msg) {super(true, "", "", msg);
}
}
package gplx.xowa.mediawiki;
import gplx.Err;
// REF.PHP:https://www.php.net/manual/en/class.exception.php
public class XophpException extends Err {
public XophpException(String message) {this(message, 0, null);}
public XophpException(String message, int code, XophpException previous) {
super(true, "", "", message);
}
}

@ -15,8 +15,8 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.mediawiki;
public class XophpFatalError extends XophpError {
public XophpFatalError(String msg) {
public class XophpFatalException extends XophpException {
public XophpFatalException(String msg) {
super(msg);
}
}

@ -1,6 +1,6 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
Copyright (C) 2012-2020 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
@ -13,7 +13,10 @@ The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.mediawiki; import gplx.*; import gplx.xowa.*;
public class XophpRuntimeException extends XophpError { public XophpRuntimeException(String msg) {super(msg);
}
}
package gplx.xowa.mediawiki;
public class XophpRuntimeException extends XophpException {
public XophpRuntimeException(String message) {super(message);}
public XophpRuntimeException(String message, int code, XophpException previous) {
super(message, code, previous);
}
}

@ -268,6 +268,9 @@ public class XophpString_ implements XophpCallbackOwner {
public static byte[] strtr(byte[] src, byte find, byte repl) {
return Bry_.Replace(src, 0, src.length, find, repl);
}
public static String strtr(String src, String find, String repl) {
return String_.Replace(src, find, repl);
}
public static byte[] str_replace(byte find, byte repl, byte[] src) {
return Bry_.Replace(src, 0, src.length, find, repl);
}
@ -322,10 +325,10 @@ public class XophpString_ implements XophpCallbackOwner {
byte nxt_byte = pad_bry[i + 1];
if (nxt_byte == Byte_ascii.Dot) {
if (i == 0) {
throw new XophpError(".. found but at start of String; src=" + pad_str);
throw new XophpException(".. found but at start of String; src=" + pad_str);
}
else if (i == pad_len - 2) {
throw new XophpError(".. found but at end of String; src=" + pad_str);
throw new XophpException(".. found but at end of String; src=" + pad_str);
}
else {
nxt_byte = pad_bry[i + 2];
@ -339,7 +342,7 @@ public class XophpString_ implements XophpCallbackOwner {
continue;
}
else {
throw new XophpError(".. found but next byte must be greater than previous byte; src=" + pad_str);
throw new XophpException(".. found but next byte must be greater than previous byte; src=" + pad_str);
}
}
}

@ -1,6 +1,6 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
Copyright (C) 2012-2020 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
@ -13,8 +13,13 @@ The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.mediawiki.extensions.Wikibase.lib.includes.Store; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.extensions.*; import gplx.xowa.mediawiki.extensions.Wikibase.*; import gplx.xowa.mediawiki.extensions.Wikibase.lib.*; import gplx.xowa.mediawiki.extensions.Wikibase.lib.includes.*;
// REF.WBASE:2020-01-19
class XomwPropertyOrderProviderException extends XophpRuntimeException { public XomwPropertyOrderProviderException(String msg) {super(msg);
}
}
package gplx.xowa.mediawiki.extensions.Wikibase.lib.includes.Store;
import gplx.xowa.mediawiki.XophpRuntimeException;
// REF.WBASE:2020-01-19
class XomwPropertyOrderProviderException extends XophpRuntimeException {
public XomwPropertyOrderProviderException(String message) {
super(message);
}
}

@ -22,7 +22,7 @@ import gplx.xowa.mediawiki.XophpArray;
import gplx.xowa.mediawiki.XophpArray_;
import gplx.xowa.mediawiki.XophpCallback;
import gplx.xowa.mediawiki.XophpCallbackOwner;
import gplx.xowa.mediawiki.XophpFatalError;
import gplx.xowa.mediawiki.XophpFatalException;
import gplx.xowa.mediawiki.XophpObject_;
import gplx.xowa.mediawiki.XophpString_;
import gplx.xowa.mediawiki.XophpType_;
@ -221,7 +221,7 @@ public class XomwHooks {
// Process the return value.
if (XophpString_.is_string(retval)) {
// String returned means error.
throw new XophpFatalError((String)retval);
throw new XophpFatalException((String)retval);
} else if (XophpObject_.is_false(retval)) {
// False was returned. Stop processing, but no error.
return false;

@ -14,13 +14,13 @@ GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.mediawiki.includes;
// MW.SRC:1.33.1
import gplx.xowa.mediawiki.XomwEnv;
import gplx.xowa.mediawiki.includes.interwiki.XomwInterwikiLookup;
import gplx.xowa.mediawiki.includes.title.XomwMediaWikiTitleCodec;
import gplx.xowa.mediawiki.languages.XomwLanguage;
// MW.SRC:1.33.1
/**
* MediaWikiServices is the service locator for the application scope of MediaWiki.
* Its implemented as a simple configurable DI container.

@ -16,6 +16,7 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
package gplx.xowa.mediawiki.includes.context;
import gplx.xowa.mediawiki.XophpObject_;
import gplx.xowa.mediawiki.XophpString_;
import gplx.xowa.mediawiki.includes.XomwOutputPage;
import gplx.xowa.mediawiki.includes.XomwTitle;
import gplx.xowa.mediawiki.includes.XomwWebRequest;
@ -105,22 +106,23 @@ public class XomwRequestContext { // implements IContextSource, MutableContext
// this.request = $request;
// }
// /**
// * @return WebRequest
// */
// public XomwWebRequest getRequest() {
// if (this.request === null) {
// global $wgCommandLineMode;
// // create the WebRequest object on the fly
// if ($wgCommandLineMode) {
// this.request = new FauxRequest([]);
// } else {
// this.request = new WebRequest();
// }
// }
//
// return this.request;
// }
/**
* @return WebRequest
*/
public XomwWebRequest getRequest() {
if (XophpObject_.is_null(this.request)) {
// XOMW:skip
// global $wgCommandLineMode;
// // create the WebRequest object on the fly
// if ($wgCommandLineMode) {
// this.request = new FauxRequest([]);
// } else {
this.request = new XomwWebRequest();
// }
}
return this.request;
}
// /**
// * @deprecated since 1.27 use a StatsdDataFactory from MediaWikiServices (preferably injected)
@ -264,7 +266,7 @@ public class XomwRequestContext { // implements IContextSource, MutableContext
*/
public XomwUser getUser() {
if (XophpObject_.is_null(this.user)) {
// this.user = XomwUser.newFromSession(this.getRequest());
this.user = XomwUser.newFromSession(this.getRequest());
}
return this.user;
@ -327,18 +329,18 @@ public class XomwRequestContext { // implements IContextSource, MutableContext
this.recursion = true;
try {
// $request = this.getRequest();
// $user = this.getUser();
//
// $code = $request.getVal('uselang', 'user');
// if ($code === 'user') {
// $code = $user.getOption('language');
// }
request = this.getRequest();
user = this.getUser();
String code = request.getVal("uselang", "user");
if (XophpString_.eq(code, "user")) {
// code = user.getOption("language");
}
// $code = self::sanitizeLangCode($code);
//
// XomwHooks.run("UserGetLanguageObject", XophpArray.New(user, &$code, this));
//
// if ($code === this.getConfig().get('LanguageCode')) {
// if ($code === this.getConfig().get("LanguageCode")) {
// this.lang = MediaWikiServices::getInstance().getContentLanguage();
// } else {
// $obj = Language::factory($code);

@ -1,49 +1,68 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.mediawiki.includes.dao; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*;
/**
* Interface for database access objects.
*
* Classes using this support a set of constants in a bitfield argument to their data loading
* functions. In general, objects should assume READ_NORMAL if no flags are explicitly given,
* though certain objects may assume READ_LATEST for common use case or legacy reasons.
*
* There are four types of reads:
* - READ_NORMAL : Potentially cached read of data (e.g. from a replica DB or stale replica)
* - READ_LATEST : Up-to-date read as of transaction start (e.g. from master or a quorum read)
* - READ_LOCKING : Up-to-date read as of now, that locks (shared) the records
* - READ_EXCLUSIVE : Up-to-date read as of now, that locks (exclusive) the records
* All record locks persist for the duration of the transaction.
*
* A special constant READ_LATEST_IMMUTABLE can be used for fetching append-only data. Such
* data is either (a) on a replica DB and up-to-date or (b) not yet there, but on the master/quorum.
* Because the data is append-only, it can never be stale on a replica DB if present.
*
* Callers should use READ_NORMAL (or pass in no flags) unless the read determines a write.
* In theory, such cases may require READ_LOCKING, though to avoid contention, READ_LATEST is
* often good enough. If UPDATE race condition checks are required on a row and expensive code
* must run after the row is fetched to determine the UPDATE, it may help to do something like:
* - a) Start transaction
* - b) Read the current row with READ_LATEST
* - c) Determine the new row (expensive, so we don't want to hold locks now)
* - d) Re-read the current row with READ_LOCKING; if it changed then bail out
* - e) otherwise, do the updates
* - f) Commit transaction
*
* @since 1.20
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2020 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.mediawiki.includes.dao;
/**
* Interface for database access objects.
*
* Classes using this support a set of constants in a bitfield argument to their data loading
* functions. In general, objects should assume READ_NORMAL if no flags are explicitly given,
* though certain objects may assume READ_LATEST for common use case or legacy reasons.
*
* There are four types of reads:
* - READ_NORMAL : Potentially cached read of data (e.g. from a replica DB or stale replica)
* - READ_LATEST : Up-to-date read as of transaction start (e.g. from master or a quorum read)
* - READ_LOCKING : Up-to-date read as of now, that locks (shared) the records
* - READ_EXCLUSIVE : Up-to-date read as of now, that locks (exclusive) the records
* All record locks persist for the duration of the transaction.
*
* A special constant READ_LATEST_IMMUTABLE can be used for fetching append-only data. Such
* data is either (a) on a replica DB and up-to-date or (b) not yet there, but on the master/quorum.
* Because the data is append-only, it can never be stale on a replica DB if present.
*
* Callers should use READ_NORMAL (or pass in no flags) unless the read determines a write.
* In theory, such cases may require READ_LOCKING, though to avoid contention, READ_LATEST is
* often good enough. If UPDATE race condition checks are required on a row and expensive code
* must run after the row is fetched to determine the UPDATE, it may help to do something like:
* - a) Start transaction
* - b) Read the current row with READ_LATEST
* - c) Determine the new row (expensive, so we don't want to hold locks now)
* - d) Re-read the current row with READ_LOCKING; if it changed then bail out
* - e) otherwise, do the updates
* - f) Commit transaction
*
* @since 1.20
*/
public interface XomwIDBAccessObject {
/** Constants for object loading bitfield flags (higher => higher QoS) */
/** @var int Read from a replica DB/non-quorum */
public static int READ_NORMAL = 0;
/** @var int Read from the master/quorum */
public static int READ_LATEST = 1;
/* @var int Read from the master/quorum and lock out other writers */
public static int READ_LOCKING = READ_LATEST | 2; // READ_LATEST (1) and "LOCK IN SHARE MODE" (2)
/** @var int Read from the master/quorum and lock out other writers and locking readers */
public static int READ_EXCLUSIVE = READ_LOCKING | 4; // READ_LOCKING (3) and "FOR UPDATE" (4)
/** @var int Read from a replica DB or without a quorum, using the master/quorum on miss */
public static int READ_LATEST_IMMUTABLE = 8;
// Convenience constant for tracking how data was loaded (higher => higher QoS)
public static int READ_NONE = -1; // not loaded yet (or the object was cleared)
}

@ -0,0 +1,38 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2020 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.mediawiki.includes.libs.services;
// MW.SRC:1.33.1
/**
* DestructibleService defines a standard interface for shutting down a service instance.
* The intended use is for a service container to be able to shut down services that should
* no longer be used, and allow such services to release any system resources.
*
* @note There is no expectation that services will be destroyed when the process (or web request)
* terminates.
*/
public interface XomwDestructibleService {
/**
* Notifies the service object that it should expect to no longer be used, and should release
* any system resources it may own. The behavior of all service methods becomes undefined after
* destroy() has been called. It is recommended that implementing classes should throw an
* exception when service methods are accessed after destroy() has been called.
*/
public void destroy();
}

@ -0,0 +1,21 @@
package gplx.xowa.mediawiki.includes.libs.services;
import gplx.String_;
import gplx.xowa.mediawiki.XophpException;
import gplx.xowa.mediawiki.XophpRuntimeException;
/**
* Exception thrown when the requested service is not known.
*/
class XomwNoSuchServiceException extends XophpRuntimeException {
/**
* @param string $serviceName
* @param Exception|null $previous
*/
public XomwNoSuchServiceException(String serviceName) {this(serviceName, null);}
public XomwNoSuchServiceException(String serviceName, XophpException previous) {
super(String_.Format("No such service: {0}", serviceName), 0, previous);
}
}

@ -0,0 +1,464 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2020 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.mediawiki.includes.libs.services;
// MW.SRC:1.33.1
import gplx.xowa.mediawiki.XophpArray;
import gplx.xowa.mediawiki.XophpArray_;
import gplx.xowa.mediawiki.XophpCallbackOwner;
import gplx.xowa.mediawiki.XophpObject_;
import gplx.xowa.mediawiki.XophpType_;
/**
* ServiceContainer provides a generic service to manage named services using
* lazy instantiation based on instantiator callback functions.
*
* Services managed by an instance of ServiceContainer may or may not implement
* a common interface.
*
* @note When using ServiceContainer to manage a set of services, consider
* creating a wrapper or a subclass that provides access to the services via
* getter methods with more meaningful names and more specific return type
* declarations.
*
* @see docs/injection.txt for an overview of using dependency injection in the
* MediaWiki code base.
*/
public class XomwServiceContainer implements XomwDestructibleService {
/**
* @var object[]
*/
private XophpArray<Object> services = new XophpArray();
/**
* @var callable[]
*/
private XophpArray<XophpCallbackOwner> serviceInstantiators = new XophpArray();
/**
* @var callable[][]
*/
private XophpArray<XophpCallbackOwner[]> serviceManipulators = new XophpArray();
/**
* @var bool[] disabled status, per service name
*/
private XophpArray<Boolean> disabled = new XophpArray();
/**
* @var array
*/
private XophpArray extraInstantiationParams;
/**
* @var bool
*/
private boolean destroyed = false;
/**
* @param array $extraInstantiationParams Any additional parameters to be passed to the
* instantiator function when creating a service. This is typically used to provide
* access to additional ServiceContainers or Config objects.
*/
public XomwServiceContainer() {this(new XophpArray());}
public XomwServiceContainer(XophpArray extraInstantiationParams) {
this.extraInstantiationParams = extraInstantiationParams;
}
/**
* Destroys all contained service instances that implement the DestructibleService
* interface. This will render all services obtained from this ServiceContainer
* instance unusable. In particular, this will disable access to the storage backend
* via any of these services. Any future call to getService() will throw an exception.
*
* @see resetGlobalInstance()
*/
public void destroy() {
for (String name : this.getServiceNames()) {
Object service = this.peekService(name);
if (service != null && XophpType_.instance_of(service, XomwDestructibleService.class)) {
((XomwDestructibleService)service).destroy();
}
}
// Break circular references due to the this reference in closures, by
// erasing the instantiator array. This allows the ServiceContainer to
// be deleted when it goes out of scope.
this.serviceInstantiators = new XophpArray();
// Also remove the services themselves, to avoid confusion.
this.services = new XophpArray();
this.destroyed = true;
}
// /**
// * @param array $wiringFiles A list of PHP files to load wiring information from.
// * Each file is loaded using PHP's include mechanism. Each file is expected to
// * return an associative array that maps service names to instantiator functions.
// */
// public function loadWiringFiles(array $wiringFiles) {
// foreach ($wiringFiles as $file) {
// // the wiring file is required to return an array of instantiators.
// $wiring = require $file;
//
// Assert::postcondition(
// is_array($wiring),
// "Wiring file $file is expected to return an array!"
// );
//
// this.applyWiring($wiring);
// }
// }
// /**
// * Registers multiple services (aka a "wiring").
// *
// * @param array $serviceInstantiators An associative array mapping service names to
// * instantiator functions.
// */
// public function applyWiring(array $serviceInstantiators) {
// Assert::parameterElementType('callable', $serviceInstantiators, '$serviceInstantiators');
//
// foreach ($serviceInstantiators as $name => $instantiator) {
// this.defineService($name, $instantiator);
// }
// }
// /**
// * Imports all wiring defined in $container. Wiring defined in $container
// * will override any wiring already defined locally. However, already
// * existing service instances will be preserved.
// *
// * @since 1.28
// *
// * @param ServiceContainer $container
// * @param string[] $skip A list of service names to skip during import
// */
// public function importWiring(ServiceContainer $container, $skip = []) {
// $newInstantiators = array_diff_key(
// $container.serviceInstantiators,
// array_flip($skip)
// );
//
// this.serviceInstantiators = array_merge(
// this.serviceInstantiators,
// $newInstantiators
// );
//
// $newManipulators = array_diff(
// array_keys($container.serviceManipulators),
// $skip
// );
//
// foreach ($newManipulators as $name) {
// if (isset(this.serviceManipulators[$name])) {
// this.serviceManipulators[$name] = array_merge(
// this.serviceManipulators[$name],
// $container.serviceManipulators[$name]
// );
// } else {
// this.serviceManipulators[$name] = $container.serviceManipulators[$name];
// }
// }
// }
/**
* Returns true if a service is defined for $name, that is, if a call to getService($name)
* would return a service instance.
*
* @param string $name
*
* @return bool
*/
public boolean hasService(String name) {
return XophpArray_.isset(this.serviceInstantiators, name);
}
/**
* Returns the service instance for $name only if that service has already been instantiated.
* This is intended for situations where services get destroyed/cleaned up, so we can
* avoid creating a service just to destroy it again.
*
* @note This is intended for internal use and for test fixtures.
* Application logic should use getService() instead.
*
* @see getService().
*
* @param string $name
*
* @return object|null The service instance, or null if the service has not yet been instantiated.
* @throws RuntimeException if $name does not refer to a known service.
*/
public Object peekService(String name) {
if (!this.hasService(name)) {
throw new XomwNoSuchServiceException(name);
}
return XophpObject_.Coalesce(this.services.Get_by(name), null);
}
/**
* @return string[]
*/
public XophpArray<String> getServiceNames() {
return XophpArray_.array_keys(this.serviceInstantiators);
}
// /**
// * Define a new service. The service must not be known already.
// *
// * @see getService().
// * @see redefineService().
// *
// * @param string $name The name of the service to register, for use with getService().
// * @param callable $instantiator Callback that returns a service instance.
// * Will be called with this ServiceContainer instance as the only parameter.
// * Any extra instantiation parameters provided to the constructor will be
// * passed as subsequent parameters when invoking the instantiator.
// *
// * @throws RuntimeException if there is already a service registered as $name.
// */
// public function defineService($name, callable $instantiator) {
// Assert::parameterType('string', $name, '$name');
//
// if (this.hasService($name)) {
// throw new ServiceAlreadyDefinedException($name);
// }
//
// this.serviceInstantiators[$name] = $instantiator;
// }
// /**
// * Replace an already defined service.
// *
// * @see defineService().
// *
// * @note This will fail if the service was already instantiated. If the service was previously
// * disabled, it will be re-enabled by this call. Any manipulators registered for the service
// * will remain in place.
// *
// * @param string $name The name of the service to register.
// * @param callable $instantiator Callback function that returns a service instance.
// * Will be called with this ServiceContainer instance as the only parameter.
// * The instantiator must return a service compatible with the originally defined service.
// * Any extra instantiation parameters provided to the constructor will be
// * passed as subsequent parameters when invoking the instantiator.
// *
// * @throws NoSuchServiceException if $name is not a known service.
// * @throws CannotReplaceActiveServiceException if the service was already instantiated.
// */
// public function redefineService($name, callable $instantiator) {
// Assert::parameterType('string', $name, '$name');
//
// if (!this.hasService($name)) {
// throw new NoSuchServiceException($name);
// }
//
// if (isset(this.services[$name])) {
// throw new CannotReplaceActiveServiceException($name);
// }
//
// this.serviceInstantiators[$name] = $instantiator;
// unset(this.disabled[$name]);
// }
//
// /**
// * Add a service manipulator callback for the given service.
// * This method may be used by extensions that need to wrap, replace, or re-configure a
// * service. It would typically be called from a MediaWikiServices hook handler.
// *
// * The manipulator callback is called just after the service is instantiated.
// * It can call methods on the service to change configuration, or wrap or otherwise
// * replace it.
// *
// * @see defineService().
// * @see redefineService().
// *
// * @note This will fail if the service was already instantiated.
// *
// * @since 1.32
// *
// * @param string $name The name of the service to manipulate.
// * @param callable $manipulator Callback function that manipulates, wraps or replaces a
// * service instance. The callback receives the new service instance and this
// * ServiceContainer as parameters, as well as any extra instantiation parameters specified
// * when constructing this ServiceContainer. If the callback returns a value, that
// * value replaces the original service instance.
// *
// * @throws NoSuchServiceException if $name is not a known service.
// * @throws CannotReplaceActiveServiceException if the service was already instantiated.
// */
// public function addServiceManipulator($name, callable $manipulator) {
// Assert::parameterType('string', $name, '$name');
//
// if (!this.hasService($name)) {
// throw new NoSuchServiceException($name);
// }
//
// if (isset(this.services[$name])) {
// throw new CannotReplaceActiveServiceException($name);
// }
//
// this.serviceManipulators[$name][] = $manipulator;
// }
/**
* Disables a service.
*
* @note Attempts to call getService() for a disabled service will result
* in a DisabledServiceException. Calling peekService for a disabled service will
* return null. Disabled services are listed by getServiceNames(). A disabled service
* can be enabled again using redefineService().
*
* @note If the service was already active (that is, instantiated) when getting disabled,
* and the service instance implements DestructibleService, destroy() is called on the
* service instance.
*
* @see redefineService()
* @see resetService()
*
* @param string $name The name of the service to disable.
*
* @throws RuntimeException if $name is not a known service.
*/
public void disableService(String name) {
this.resetService(name);
this.disabled.Set(name, true);
}
/**
* Resets a service by dropping the service instance.
* If the service instances implements DestructibleService, destroy()
* is called on the service instance.
*
* @warning This is generally unsafe! Other services may still retain references
* to the stale service instance, leading to failures and inconsistencies. Subclasses
* may use this method to reset specific services under specific instances, but
* it should not be exposed to application logic.
*
* @note This is declared final so subclasses can not interfere with the expectations
* disableService() has when calling resetService().
*
* @see redefineService()
* @see disableService().
*
* @param string $name The name of the service to reset.
* @param bool $destroy Whether the service instance should be destroyed if it exists.
* When set to false, any existing service instance will effectively be detached
* from the container.
*
* @throws RuntimeException if $name is not a known service.
*/
protected void resetService(String name) {this.resetService(name, true);}
protected void resetService(String name, boolean destroy) {
// Assert::parameterType('string', $name, '$name');
Object instance = this.peekService(name);
if (destroy && XophpType_.instance_of(instance, XomwDestructibleService.class)) {
((XomwDestructibleService)instance).destroy();
}
XophpArray_.unset(this.services, name);
XophpArray_.unset(this.disabled, name);
}
/**
* Returns a service object of the kind associated with $name.
* Services instances are instantiated lazily, on demand.
* This method may or may not return the same service instance
* when called multiple times with the same $name.
*
* @note Rather than calling this method directly, it is recommended to provide
* getters with more meaningful names and more specific return types, using
* a subclass or wrapper.
*
* @see redefineService().
*
* @param string $name The service name
*
* @throws NoSuchServiceException if $name is not a known service.
* @throws ContainerDisabledException if this container has already been destroyed.
* @throws ServiceDisabledException if the requested service has been disabled.
*
* @return object The service instance
*/
public Object getService(String name) {
if (this.destroyed) {
// throw new XomwContainerDisabledException();
}
if (XophpArray_.isset(this.disabled, name)) {
// throw new XomwServiceDisabledException($name);
}
if (!XophpArray_.isset(this.services, name)) {
this.services.Set(name, this.createService(name));
}
return this.services.Get_by(name);
}
/**
* @param string $name
*
* @throws InvalidArgumentException if $name is not a known service.
* @return object
*/
private Object createService(String name) {
// if (isset(this.serviceInstantiators[$name])) {
// $service = (this.serviceInstantiators[$name])(
// this,
// ...this.extraInstantiationParams
// );
//
// if (isset(this.serviceManipulators[$name])) {
// foreach (this.serviceManipulators[$name] as $callback) {
// $ret = call_user_func_array(
// $callback,
// array_merge([ $service, this ], this.extraInstantiationParams)
// );
//
// // If the manipulator callback returns an object, that object replaces
// // the original service instance. This allows the manipulator to wrap
// // or fully replace the service.
// if ($ret !== null) {
// $service = $ret;
// }
// }
// }
//
// // NOTE: when adding more wiring logic here, make sure importWiring() is kept in sync!
// } else {
// throw new NoSuchServiceException($name);
// }
//
// return $service;
return null;
}
/**
* @param string $name
* @return bool Whether the service is disabled
* @since 1.28
*/
public boolean isServiceDisabled(String name) {
return XophpArray_.isset(this.disabled, name);
}
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save