Private classes. Hiding in php

    In php, as in most other OOP languages, there are visibility modifiers. These are the keywords public, protected and private. But they apply exclusively to properties, methods or constants. These modifiers are closely related to the ability to encapsulate data. It is worth noting that in languages ​​such as java, C #, go ( https://golang.org/doc/go1.4#internalpackages ), ruby ​​( https://habr.com/post/419969/ ), crystal ( https : //crystal-lang.org/reference/syntax_and_semantics/visibility.html) it is possible to limit the scope of visibility of packages (packages) or classes \ types. In php, it is not possible to limit the scope for classes - any connected class is accessible from any other class. However, you can emulate this feature using several tricks.


    For which you may need to hide at the class level:


    • Library service classes (helpers) - do not litter the library API with non-meaningful internal classes.
    • Encapsulation with hiding internal business logic objects, for example, prohibiting the direct generation of dependent objects, bypassing a more general class.

    Separately, you can select the partition of the "large" classes into small objects. It is considered good practice to limit the complexity (and the number of lines) of both individual methods and classes. The number of lines here goes as one of the markers, that the class method or the class itself takes on unnecessary responsibility. When refactoring a public method, we move parts of it to private \ protected methods. But when, for one reason or another, a class grows and we separate a separate entity from it, these most private \ protected classes are transferred to a separate class, thus we indirectly open access to methods that were previously limited to the scope of one class.


    Now, the actual methods of emulation of concealment.


    At the level of agreement code design


    Using PHPDoc comments, you can mark a class, trait, or interface as internal( http://docs.phpdoc.org/references/phpdoc/tags/internal.html ). However, some IDEs (for example, PhpStorm) can understand such tags.


    Use runtime information


    During execution of the code, you can check where the class constructor was called from. For example, through the method debug_backtrace( http://php.net/manual/ru/function.debug-backtrace.php ) or use the same Xdebug functionality to control the code in the dev \ test environment. An example of a framed solution is here ( https://coderwall.com/p/ixvnga/how-emulates-private-class-concept-in-php ).


    use debug_backtrace
    /**
     * The private class
     */finalclassPrivateClass{
        /**
         * defines the only class able to instantiate the current one
         * 
         * @var string
         */private $allowedConsumer = 'AllowedPrivateClassConsumer';
        /**
         * constructor
         * 
         * @throws Exception
         */publicfunction__construct(){
            /**
             * here comes the privacy filter, it could be extracted to a private method
             * or to a static method of another class with few adjustments
             */
            $builder = debug_backtrace();
            if (count($builder) < 2 || 
                !isset($builder[1]['class']) ||
                $builder[1]['class'] !== $this->allowedConsumer)
            {
                thrownewException('Need to be instantiated by '.$this->allowedConsumer);
            }
        }
    }

    Use anonymous classes


    Relatively new features in php are anonymous classes ( http://php.net/manual/ru/language.oop5.anonymous.php ). By describing an anonymous class inside a protected method, we seek to hide it. In order not to enclose the class definition inside a function, you can describe the "private" class in a separate file as abstract, and already expand it in the definition of an anonymous class. A good example of using this method is at this link ( https://markbakeruk.net/2018/06/25/using-php-anonymous-classes-as-package-private-classes/ ).


    Based on the material found, it is clear that the class-hiding functionality is to some extent in demand (and exists in many languages), but the practice of its use is very limited, possibly lacking a description of examples in various "best practices", template collections and similar sources. Which in my opinion is rather strange, that there is an emphasis on hiding the internal methods and properties of objects, but almost no one pays attention that larger logical pieces of code in the form of utility classes of libraries or domain domains remain in the global visibility space.


    Also popular now: