26769

Changing the visibility scope of parent methods in child classes

Question:

I've got a Validator class and a UserValidator class which extends from it.

My Validator has a public method setRule(...) with public visibility.

When I extend from it I want to change the visibility of the setRule(...) parent method to private/protected <strong>within</strong> the child so that it's only visible for the child and no outsiders can call this method <strong>from</strong> the child.

Is that possible? If so, how could I achieve it?

Answer1:

From an architectural point of view this is not recommended. As already stated in the comments the clean way would be to set your method to protected so only children can access it.

I cannot think of a single use case that would put me in the need to call a public method on a parent class but where I am not allowed to call it on the child class.

That's against the Open/Closed principle. Classes should be open for extension, but not for modification.

Since that was not the question I'll provide a way how that can be achieved though. But note:

<ul><li>This method makes use of an extra class which will be responsible for the instantiation</li> <li>It's a hack. This solution will not make use of PHP's native language features when throwing accessibility errors.</li> </ul>

First let's define the classes you already had

<?php class Validator { public function setRule() { echo "Hello World"; } } class UserValidator extends Validator { public $prop = 'PROPERTY'; }

There's nothing special here. So let's go on and create a custom exception class for the visibility error.

<?php class MethodNotAccessibleException extends Exception {}

This exception will be thrown when we try to invoke a "pseudo-private" method on the child class.

Now we want to create the Class that will be responsible for instantiating your child class. It is basically just a wrapper that defines a lock property which holds method names that should not be accessible.

<?php class PrivateInstanceCreator { protected $reflectionClass; protected $lock = []; protected $instance; public function __construct($classname, $args = []) { // We'll store an instance of the reflection class // and an instance of the real class $this->reflectionClass = new ReflectionClass($classname); $this->instance = $this->reflectionClass->newInstanceArgs($args); return $this; } // The lock method is able to make a method on the // target class "pseudo-private" public function lock($method) { $this->lock[] = $method; return $this; } // Some real magic is going on here // Remember. This class is a wrapper for the real class // if a method is invoked we look for the method // in the real instance and invoke it... public function __call($method, $args) { // ... but as soon as this method is defined as // locked, we'll raise an exception that the method // is private if(in_array($method, $this->lock)) { $reflectionMethod = $this->reflectionClass->getMethod($method); if($reflectionMethod->isPublic()) throw new MethodNotAccessibleException('Method: __' . $method . '__ is private and could not be invoked'); } return call_user_func_array([$this->instance, $method], $args); } // The same goes for properties // But in this case we'll do no protection public function __get($prop) { return $this->instance->{$prop}; } }

Our final step is the instantiation.

<?php $userValidator = new PrivateInstanceCreator('UserValidator', []); $userValidator->lock('setRule'); $userValidator->setRule(); //Will throw an exception

Instead of instantiating the class directly we'll do it by using our custom wrapper class. Of course you could handle it in the child class itself, but that's a way to accomplish your task without touching the classes directly.

Having said that, <strong>it is still a dirty hack whose usage should be avoided if possible</strong>. If you would instantiate the child class directly the inherited methods would still be public.

So if a developer has no knowlege about the wrapper class he'll have a hard time to figure out how to instantiate the child class properly.

<strong>Update:</strong>

To make the child class uninstantiable directly you can set the constructor to private and call newInstanceWithoutConstructor() from the reflection class, which is even dirtier, since that would make <a href="http://www.phptherightway.com/#dependency_injection" rel="nofollow">Dependency Injection</a> for the class completely impossible. I'm just mentioning it for completenesses sake. <strong>Usage is still not recommended</strong>

Recommend

  • FPDF linebreak in PHP
  • How to change the layering of KML and Tile Overlays in Google Maps?
  • Getting the “Large Text” font size from Settings/Accessibility?
  • Tap command on a button not working in UIAutomation
  • How to create struct based properties with Roslyn
  • iOS - Can I disable accessibility on cell.textLabel.text?
  • How does Lastpass autofill work in Android?
  • Android Studio cannot resolve getDefaultProguardFile
  • How does `std::terminate` know to treat `std::exception`s specially?
  • Handling exceptions in a class library enveloping a device driver
  • Why does .addView throw this parent/child exception?
  • API (curl)Command to Approve a promoted build Job in Jenkins
  • UML diagram generator in Visual Studio 2010
  • Embedded Glassfish JPA Datasource connection fail
  • Better Indy for Dephi 2007
  • Compare struct to a constant in C
  • Activation Function choice for Neural network
  • NUnit 3.0 TestCase const custom object arguments
  • Does Mobilefirst provide a provision to access web services directly?
  • How to revert to previous XCode version?
  • What does 'Language neutral' mean with regard to MAKELANGID?
  • Android activity accessing service's static reference before the service is ready
  • Custom Tabgroup Appcelerator
  • Make VS2015 use angular-cli ng at build time in a .NET project
  • Sencha Touch 2.0 Controller refs attribute not working?
  • Switching to Release Build causes runtime error in Web Reference
  • why do I get the error when installing the gem 'pg'? [duplicate]
  • When to use `image` and when to use `Matrix` in Emgu CV?
  • What is the “return” in scheme?
  • Excel - Autoshape get it's name from cell (value)
  • Check if a string to interpolate provides expected placeholders
  • Where to put my custom functions in Wordpress?
  • Which linear programming package should I use for high numbers of constraints and “warm starts” [clo
  • Upload files with Ajax and Jquery
  • Do I've to free mysql result after storing it?
  • RestKit - RKRequestDelegate does not exist
  • Numpy divide by zero. Why?
  • Traverse Array and Display in markup
  • A cron job substitute?
  • json Serialization in asp