Florianschmidtwelzow has uploaded a new change for review.

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

Change subject: Use clone to clone a MutableContext object in DerivativeContext
......................................................................

Use clone to clone a MutableContext object in DerivativeContext

Using local class member variables to hold instances of objects set by one of
the set*-functions works very well for the get*-functions, but doesn't work,
if a function uses the object behind this function in another context. The 
example
is the getWikiPage() which uses the title object returned by the getTitle() 
method
to construct the WikiPage object. If a user of DerivativeContext uses the object
to set another title as in the source context object (passed to the 
DerivativeContext
constructor) without setting a WikiPage object, DerivativeContext::getWikiPage()
will return the result of getWikiPage() of the context object passed in the 
constructor.
In this case, the Title object of this context will be used, instead of the 
Title
object set in the DerivativeContext (like the user can expect).

To fix this, this change removes the handling of local object and instead 
changes
the behaviour of the constructor a little bit. Instead of setting the original
context object as the context of the DerivativeContext, it will clone this 
source
context object and set this instead. The set*-functions now simply pass the call
to this newly cloned context object. The get*-functions will do the same. Using
this approach, the DerivativeContext should be much more consistently while 
handling
other objects as the source context object without touching the source context 
object.

The added test for DerivativeContext checks the behaviour for 
setTitle()/getWikiPage(),
and gives a good example of the origin problem.

Bug: T124060
Change-Id: I730255acb361bbdcca26cce2cc5c178db8f24e63
---
M includes/context/DerivativeContext.php
A tests/phpunit/includes/context/DerivativeContextTest.php
2 files changed, 57 insertions(+), 237 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/76/264976/1

diff --git a/includes/context/DerivativeContext.php 
b/includes/context/DerivativeContext.php
index 1b881e4..74a7e9d 100644
--- a/includes/context/DerivativeContext.php
+++ b/includes/context/DerivativeContext.php
@@ -27,61 +27,18 @@
  * @since 1.19
  */
 class DerivativeContext extends ContextSource implements MutableContext {
-       /**
-        * @var WebRequest
-        */
-       private $request;
-
-       /**
-        * @var Title
-        */
-       private $title;
-
-       /**
-        * @var WikiPage
-        */
-       private $wikipage;
-
-       /**
-        * @var OutputPage
-        */
-       private $output;
-
-       /**
-        * @var User
-        */
-       private $user;
-
-       /**
-        * @var Language
-        */
-       private $lang;
-
-       /**
-        * @var Skin
-        */
-       private $skin;
-
-       /**
-        * @var Config
-        */
-       private $config;
-
-       /**
-        * @var Stats
-        */
-       private $stats;
-
-       /**
-        * @var Timing
-        */
-       private $timing;
 
        /**
         * Constructor
-        * @param IContextSource $context Context to inherit from
+        * @param MutableContext $context MutableContext to inherit from
         */
-       public function __construct( IContextSource $context ) {
+       public function __construct( MutableContext $context ) {
+               // Clone the original context object so we can change it 
without changing the
+               // source context one.
+               $context = clone $context;
+
+               // set this cloned context object as the context of this 
DerivativeContext instance,
+               // so any set*-call will be done on this one, like any get*-call
                $this->setContext( $context );
        }
 
@@ -91,46 +48,7 @@
         * @param Config $s
         */
        public function setConfig( Config $s ) {
-               $this->config = $s;
-       }
-
-       /**
-        * Get the Config object
-        *
-        * @return Config
-        */
-       public function getConfig() {
-               if ( !is_null( $this->config ) ) {
-                       return $this->config;
-               } else {
-                       return $this->getContext()->getConfig();
-               }
-       }
-
-       /**
-        * Get the stats object
-        *
-        * @return BufferingStatsdDataFactory
-        */
-       public function getStats() {
-               if ( !is_null( $this->stats ) ) {
-                       return $this->stats;
-               } else {
-                       return $this->getContext()->getStats();
-               }
-       }
-
-       /**
-        * Get the timing object
-        *
-        * @return Timing
-        */
-       public function getTiming() {
-               if ( !is_null( $this->timing ) ) {
-                       return $this->timing;
-               } else {
-                       return $this->getContext()->getTiming();
-               }
+               $this->getContext()->setConfig( $s );
        }
 
        /**
@@ -139,20 +57,7 @@
         * @param WebRequest $r
         */
        public function setRequest( WebRequest $r ) {
-               $this->request = $r;
-       }
-
-       /**
-        * Get the WebRequest object
-        *
-        * @return WebRequest
-        */
-       public function getRequest() {
-               if ( !is_null( $this->request ) ) {
-                       return $this->request;
-               } else {
-                       return $this->getContext()->getRequest();
-               }
+               $this->getContext()->setRequest( $r );
        }
 
        /**
@@ -161,38 +66,7 @@
         * @param Title $t
         */
        public function setTitle( Title $t ) {
-               $this->title = $t;
-       }
-
-       /**
-        * Get the Title object
-        *
-        * @return Title|null
-        */
-       public function getTitle() {
-               if ( !is_null( $this->title ) ) {
-                       return $this->title;
-               } else {
-                       return $this->getContext()->getTitle();
-               }
-       }
-
-       /**
-        * Check whether a WikiPage object can be get with getWikiPage().
-        * Callers should expect that an exception is thrown from getWikiPage()
-        * if this method returns false.
-        *
-        * @since 1.19
-        * @return bool
-        */
-       public function canUseWikiPage() {
-               if ( $this->wikipage !== null ) {
-                       return true;
-               } elseif ( $this->title !== null ) {
-                       return $this->title->canExist();
-               } else {
-                       return $this->getContext()->canUseWikiPage();
-               }
+               $this->getContext()->setTitle( $t );
        }
 
        /**
@@ -202,24 +76,7 @@
         * @param WikiPage $p
         */
        public function setWikiPage( WikiPage $p ) {
-               $this->wikipage = $p;
-       }
-
-       /**
-        * Get the WikiPage object.
-        * May throw an exception if there's no Title object set or the Title 
object
-        * belongs to a special namespace that doesn't have WikiPage, so use 
first
-        * canUseWikiPage() to check whether this method can be called safely.
-        *
-        * @since 1.19
-        * @return WikiPage
-        */
-       public function getWikiPage() {
-               if ( !is_null( $this->wikipage ) ) {
-                       return $this->wikipage;
-               } else {
-                       return $this->getContext()->getWikiPage();
-               }
+               $this->getContext()->setWikiPage( $p );
        }
 
        /**
@@ -228,20 +85,7 @@
         * @param OutputPage $o
         */
        public function setOutput( OutputPage $o ) {
-               $this->output = $o;
-       }
-
-       /**
-        * Get the OutputPage object
-        *
-        * @return OutputPage
-        */
-       public function getOutput() {
-               if ( !is_null( $this->output ) ) {
-                       return $this->output;
-               } else {
-                       return $this->getContext()->getOutput();
-               }
+               $this->getContext()->setOutput( $o );
        }
 
        /**
@@ -250,20 +94,7 @@
         * @param User $u
         */
        public function setUser( User $u ) {
-               $this->user = $u;
-       }
-
-       /**
-        * Get the User object
-        *
-        * @return User
-        */
-       public function getUser() {
-               if ( !is_null( $this->user ) ) {
-                       return $this->user;
-               } else {
-                       return $this->getContext()->getUser();
-               }
+               $this->getContext()->setUser( $u );
        }
 
        /**
@@ -274,29 +105,7 @@
         * @since 1.19
         */
        public function setLanguage( $l ) {
-               if ( $l instanceof Language ) {
-                       $this->lang = $l;
-               } elseif ( is_string( $l ) ) {
-                       $l = RequestContext::sanitizeLangCode( $l );
-                       $obj = Language::factory( $l );
-                       $this->lang = $obj;
-               } else {
-                       throw new MWException( __METHOD__ . " was passed an 
invalid type of data." );
-               }
-       }
-
-       /**
-        * Get the Language object
-        *
-        * @return Language
-        * @since 1.19
-        */
-       public function getLanguage() {
-               if ( !is_null( $this->lang ) ) {
-                       return $this->lang;
-               } else {
-                       return $this->getContext()->getLanguage();
-               }
+               $this->getContext()->setLanguage( $l );
        }
 
        /**
@@ -305,36 +114,8 @@
         * @param Skin $s
         */
        public function setSkin( Skin $s ) {
-               $this->skin = clone $s;
-               $this->skin->setContext( $this );
-       }
-
-       /**
-        * Get the Skin object
-        *
-        * @return Skin
-        */
-       public function getSkin() {
-               if ( !is_null( $this->skin ) ) {
-                       return $this->skin;
-               } else {
-                       return $this->getContext()->getSkin();
-               }
-       }
-
-       /**
-        * Get a message using the current context.
-        *
-        * This can't just inherit from ContextSource, since then
-        * it would set only the original context, and not take
-        * into account any changes.
-        *
-        * @param mixed $args,... Arguments to wfMessage
-        * @return Message
-        */
-       public function msg() {
-               $args = func_get_args();
-
-               return call_user_func_array( 'wfMessage', $args )->setContext( 
$this );
+               $skin = clone $s;
+               $skin->setContext( $this );
+               $this->getContext()->setSkin( $skin );
        }
 }
diff --git a/tests/phpunit/includes/context/DerivativeContextTest.php 
b/tests/phpunit/includes/context/DerivativeContextTest.php
new file mode 100644
index 0000000..02a2470
--- /dev/null
+++ b/tests/phpunit/includes/context/DerivativeContextTest.php
@@ -0,0 +1,39 @@
+<?php
+
+class DerivativeContextTest extends MediaWikiTestCase {
+
+       public function testContextTitle() {
+               // the title used for the RequestContext object
+               $title = Title::newFromText( 'TestTitle' );
+               // the Title used for the DerivativeContext object
+               $newTitle = Title::newFromText( 'TestTitle2' );
+
+               $context = new RequestContext();
+               $context->setTitle( $title );
+
+               $derivativeContext = new DerivativeContext( $context );
+               // check, if both WikiPage object are constructed with the same 
Title object
+               $this->assertEquals(
+                       
$derivativeContext->getWikiPage()->getTitle()->getText(),
+                       $context->getWikiPage()->getTitle()->getText(),
+                       'The WikiPage returned by 
DerivativeContext::getWikiPage() is the same' .
+                       'as the one returned by RequestContext::getWikiPage().'
+               );
+
+               // set the own Title to the DerivativeContext
+               $derivativeContext->setTitle( $newTitle );
+
+               $this->assertEquals(
+                       
$derivativeContext->getWikiPage()->getTitle()->getText(),
+                       'TestTitle2',
+                       'The WikiPage returned by 
DerivativeContext::getWikiPage() is constructed using' .
+                       'the title set by DerivativeContext::setTitle().'
+               );
+               $this->assertNotEquals(
+                       
$derivativeContext->getWikiPage()->getTitle()->getText(),
+                       $context->getWikiPage()->getTitle()->getText(),
+                       'The WikiPage returned by 
DerivativeContext::getWikiPage() isn\'t the same' .
+                       'as the one returned by RequestContext::getWikiPage().'
+               );
+       }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/264976
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I730255acb361bbdcca26cce2cc5c178db8f24e63
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Florianschmidtwelzow <florian.schmidt.stargatewis...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to