private function _processCurrentClass()
    {
        // We must always begin by reflecting its "JSON_PROPERTY_MAP" class
        // constant to determine if this particular class in the hierarchy
        // re-defines its value (compared to inheriting it from its "extends"
        // parent). This protects against accidentally re-parsing inherited
        // constants, which would both waste time AND would be VERY dangerous,
        // since that would re-interpret all inherited relative properties (ones
        // pointing at classes relative to the class which ACTUALLY declared
        // that constant) as if they were relative to the INHERITING class
        // instead, which is VERY wrong (and could lead to classes being either
        // mismapped or "not found"). Luckily the code below fully protects us
        // against that scenario, by detecting if our value is "ours" or not.
        try {
            // Use different techniques based on what their PHP supports.
            $foundConstant = false;
            if (version_compare(PHP_VERSION, '7.1.0') >= 0) {
                // In PHP 7.1.0 and higher, they've finally added "reflection
                // constants" which allow us to get accurate extra information.
                $reflectionConstant = $this->_currentClassInfo['reflector']
                                    ->getReflectionConstant('JSON_PROPERTY_MAP');
                if ($reflectionConstant !== false) {
                    $foundConstant = true;

                    // Just read its value. We don't have to care about its
                    // isPrivate() flags etc. It lets us read the value anyway.
                    $rawClassPropertyMap = $reflectionConstant->getValue();

                    // Use PHP7.1's ReflectionClassConstant's ability to tell us
                    // EXACTLY which class declared the current (inherited or
                    // new) value for the constant. If OUR class didn't declare
                    // it, then we know that it was inherited and should NOT be
                    // parsed again. But if we DID declare its value, then we
                    // know that we MUST parse it ("has different constant").
                    // NOTE: This method is 100% accurate even when re-declared
                    // to the exact same value as the inherited (parent) value!
                    $hasDifferentConstant = ($reflectionConstant
                                             ->getDeclaringClass()->getName()
                                             === $this->_currentClassInfo['className']);
                }
            } else {
                // In older PHP versions, we're pretty limited... we don't get
                // ANY extra information about the constants. We just get their
                // values. And if we try to query a specific constant, we get
                // FALSE if it doesn't exist (which is indistinguishable from it
                // actually having FALSE values). So we MUST get an array of all
                // constants to be able to check whether it TRULY exists or not.
                $classConstants = $this->_currentClassInfo['reflector']
                                ->getConstants();
                if (array_key_exists('JSON_PROPERTY_MAP', $classConstants)) {
                    $foundConstant = true;

                    // Read its value. Unfortunately old versions of PHP don't
                    // give us ANY information about which class declared it
                    // (meaning whether it was inherited or re-declared here).
                    $rawClassPropertyMap = $classConstants['JSON_PROPERTY_MAP'];

                    // MAGIC: This is the best we can do on old PHP versions...
                    // The "!==" ensures that this constant really differs from
                    // its parent. In case of arrays, they are only treated as
                    // identical if both arrays have the same key & value types
                    // and values in the exact same order and same count(). And
                    // it checks recursively!
                    //
                    // NOTE: This method is great, but it's not as accurate as
                    // the perfect PHP 7.1 method. It actually has a VERY TINY
                    // issue where it will fail to detect a new constant: If you
                    // manually re-declare a constant to EXACTLY the same value
                    // as what you would have inherited from your parent
                    // ("extends") hierarchy, then we won't be able to detect
                    // that you have a new value. Instead, you will inherit the
                    // compiled parent class map as if you hadn't declared any
                    // value of your own at all. In almost all cases, that won't
                    // matter, and will give you the same result. It only
                    // matters in a very tiny case which probably won't happen
                    // to anybody in real-world usage:
                    //
                    // namespace A { class A { "foo":"B[]" } class B {} }
                    // namespace Z { class Z extends \A\A { "foo":"B[]" } class B {} }
                    //
                    // In that situation, Z\Z would inherit the constant from
                    // A\A, which compiled the relative-class "foo" property as
                    // "\A\B". And when we look at Z's own constant, we would
                    // see a 100% identical JSON_PROPERTY_MAP constant compared
                    // to A\A, so we would assume that since all the array keys
                    // and values match exactly, its constant "was inherited".
                    // Therefore the "identical" constant of Z will not be
                    // parsed. And Z's foo will therefore also link to "\A\B",
                    // instead of "\Z\B".
                    //
                    // In reality, I doubt that ANY user would EVER have such a
                    // weird inheritance structure, with "extends" across
                    // namespaces and relative paths to classes that exist in
                    // both namespaces, etc. And the problem above would not be
                    // able to happen as long as their "Z\Z" map has declared
                    // ANY other value too (or even just changed the order of
                    // the declarations), so that we detect that their constant
                    // differs in "Z\Z". So 99.9999% of users will be safe.
                    //
                    // And no... we CAN'T simply always re-interpret it, because
                    // then we'd get a FAR more dangerous bug, which means that
                    // ALL relative properties would re-compile every time they
                    // are inherited, "as if they had been declared by us".
                    //
                    // So no, this method really is the BEST that PHP < 7.1 has.
                    $hasDifferentConstant = ($rawClassPropertyMap
                                             !== $this->_previousMapConstantValue);

                    // Update the "previous constant value" since we will be the
                    // new "previous" value after this iteration.
                    $this->_previousMapConstantValue = $rawClassPropertyMap;
                }
            }
            if (!$foundConstant) {
                // The constant doesn't exist. Should never be able to happen
                // since the class inherits from LazyJsonMapper.
                throw new ReflectionException(
                    // NOTE: This exception message mimics PHP's own reflection
                    // error message style.
                    'Constant JSON_PROPERTY_MAP does not exist'
                );
            }
        } catch (ReflectionException $e) {
            // Unable to read the map constant from the class...
            throw new BadPropertyMapException(sprintf(
                'Reflection of JSON_PROPERTY_MAP constant failed for class "%s". Reason: "%s".',
                $this->_currentClassInfo['strictClassName'], $e->getMessage()
            ));
        }

        // If we've already parsed that class before, it means that we've
        // already fully parsed it AND its parents. If so, just use the cache.
        if (isset($this->_propertyMapCache->classMaps[$this->_currentClassInfo['className']])) {
            // Overwrite our whole "_currentClassPropertyMap" with the one from
            // our parent instead. That's faster than merging the arrays, and
            // guarantees PERFECT PropertyDefinition instance re-usage.
            // NOTE: To explain it, imagine class B extending from A. A has
            // already been parsed and cached. We then instantiate B for the
            // first time and we need to build B's definitions. By copying the
            // cache from A (its parent), we'll re-use all of A's finished
            // PropertyDefinition objects in B and throughout any other chain
            // that derives from the same base object (A). This holds true for
            // ALL objects that inherit (or "import") ANYTHING (such as
            // something that later refers to "B"), since we ALWAYS resolve the
            // chains top-down from the base class to the deepest child class,
            // and therefore cache the base (hierarchically shared) definitions
            // FIRST.
            $this->_currentClassPropertyMap = $this->_propertyMapCache->classMaps[
                $this->_currentClassInfo['className']
            ];
        } else {
            // We have not parsed/encountered this class before...

            // Only parse its class constant if it differs from its inherited
            // parent's constant value. Otherwise we'll keep the parent's map
            // (the value that's currently in "_currentClassPropertyMap").
            if ($hasDifferentConstant) {
                // Process and validate the JSON_PROPERTY_MAP of the class.
                if (!is_array($rawClassPropertyMap)) {
                    throw new BadPropertyMapException(sprintf(
                        'Invalid JSON property map in class "%s". The map must be an array.',
                        $this->_currentClassInfo['strictClassName']
                    ));
                }
                foreach ($rawClassPropertyMap as $propName => $propDefStr) {
                    // Process the current entry and add it to the current
                    // class' compiled property map if the entry is new/diff.
                    $this->_processPropertyMapEntry( // Throws.
                        $propName,
                        $propDefStr
                    );
                }
            }

            // Mark the fact that we have compiled this class, so that we'll be
            // able to know which classes we have compiled later. This list will
            // bubble through the compile-call-hierarchy as-needed to keep track
            // of EVERY compiled class during the current root compile()-run.
            $this->compiledClasses[$this->_currentClassInfo['className']] = true;

            // Now cache the final property-map for this particular class in the
            // inheritance chain... Note that if it had no new/different map
            // declarations of its own (despite having a different constant),
            // then we're still simply re-using the exact property-map objects
            // of its parent class here again.
            $this->_propertyMapCache->classMaps[
                $this->_currentClassInfo['className']
            ] = $this->_currentClassPropertyMap;
        } // End of class-map "lookup-or-compilation".

        // IMPORTANT: We've finished processing a class. If this was one of the
        // classes that WE locked, we MUST NOW unlock it, but ONLY if we
        // successfully processed the class (added it to the cache). Otherwise
        // we'll unlock it at the end (where we unlock any stragglers) instead.
        // NOTE: We AREN'T allowed to unlock classes we didn't lock.
        // NOTE: Unlocking is IMPORTANT, since it's necessary for being able to
        // import classes from diverging branches with shared inheritance
        // ancestors. If we don't release the parent-classes one by one as we
        // resolve them, then we would never be able to import classes from
        // other branches of our parent/class hierarchy, since the imports would
        // see that one of their parents (from our hierarchy) is still locked
        // and would refuse to compile themselves.
        if (isset($this->_ourCompilerClassLocks[$this->_currentClassInfo['className']])) {
            unset($this->_ourCompilerClassLocks[$this->_currentClassInfo['className']]);
            unset($this->_propertyMapCache->compilerLocks[$this->_currentClassInfo['className']]);
        }
    }
private function _processCurrentClass()
    {
        // We must always begin by reflecting its "JSON_PROPERTY_MAP" class
        // constant to determine if this particular class in the hierarchy
        // re-defines its value (compared to inheriting it from its "extends"
        // parent). This protects against accidentally re-parsing inherited
        // constants, which would both waste time AND would be VERY dangerous,
        // since that would re-interpret all inherited relative properties (ones
        // pointing at classes relative to the class which ACTUALLY declared
        // that constant) as if they were relative to the INHERITING class
        // instead, which is VERY wrong (and could lead to classes being either
        // mismapped or "not found"). Luckily the code below fully protects us
        // against that scenario, by detecting if our value is "ours" or not.
        try {
            // Use different techniques based on what their PHP supports.
            $foundConstant = false;
            if (version_compare(PHP_VERSION, '7.1.0') >= 0) {
                // In PHP 7.1.0 and higher, they've finally added "reflection
                // constants" which allow us to get accurate extra information.
                $reflectionConstant = $this->_currentClassInfo['reflector']
                                    ->getReflectionConstant('JSON_PROPERTY_MAP');
                if ($reflectionConstant !== false) {
                    $foundConstant = true;

                    // Just read its value. We don't have to care about its
                    // isPrivate() flags etc. It lets us read the value anyway.
                    $rawClassPropertyMap = $reflectionConstant->getValue();

                    // Use PHP7.1's ReflectionClassConstant's ability to tell us
                    // EXACTLY which class declared the current (inherited or
                    // new) value for the constant. If OUR class didn't declare
                    // it, then we know that it was inherited and should NOT be
                    // parsed again. But if we DID declare its value, then we
                    // know that we MUST parse it ("has different constant").
                    // NOTE: This method is 100% accurate even when re-declared
                    // to the exact same value as the inherited (parent) value!
                    $hasDifferentConstant = ($reflectionConstant
                                             ->getDeclaringClass()->getName()
                                             === $this->_currentClassInfo['className']);
                }
            } else {
                // In older PHP versions, we're pretty limited... we don't get
                // ANY extra information about the constants. We just get their
                // values. And if we try to query a specific constant, we get
                // FALSE if it doesn't exist (which is indistinguishable from it
                // actually having FALSE values). So we MUST get an array of all
                // constants to be able to check whether it TRULY exists or not.
                $classConstants = $this->_currentClassInfo['reflector']
                                ->getConstants();
                if (array_key_exists('JSON_PROPERTY_MAP', $classConstants)) {
                    $foundConstant = true;

                    // Read its value. Unfortunately old versions of PHP don't
                    // give us ANY information about which class declared it
                    // (meaning whether it was inherited or re-declared here).
                    $rawClassPropertyMap = $classConstants['JSON_PROPERTY_MAP'];

                    // MAGIC: This is the best we can do on old PHP versions...
                    // The "!==" ensures that this constant really differs from
                    // its parent. In case of arrays, they are only treated as
                    // identical if both arrays have the same key & value types
                    // and values in the exact same order and same count(). And
                    // it checks recursively!
                    //
                    // NOTE: This method is great, but it's not as accurate as
                    // the perfect PHP 7.1 method. It actually has a VERY TINY
                    // issue where it will fail to detect a new constant: If you
                    // manually re-declare a constant to EXACTLY the same value
                    // as what you would have inherited from your parent
                    // ("extends") hierarchy, then we won't be able to detect
                    // that you have a new value. Instead, you will inherit the
                    // compiled parent class map as if you hadn't declared any
                    // value of your own at all. In almost all cases, that won't
                    // matter, and will give you the same result. It only
                    // matters in a very tiny case which probably won't happen
                    // to anybody in real-world usage:
                    //
                    // namespace A { class A { "foo":"B[]" } class B {} }
                    // namespace Z { class Z extends \A\A { "foo":"B[]" } class B {} }
                    //
                    // In that situation, Z\Z would inherit the constant from
                    // A\A, which compiled the relative-class "foo" property as
                    // "\A\B". And when we look at Z's own constant, we would
                    // see a 100% identical JSON_PROPERTY_MAP constant compared
                    // to A\A, so we would assume that since all the array keys
                    // and values match exactly, its constant "was inherited".
                    // Therefore the "identical" constant of Z will not be
                    // parsed. And Z's foo will therefore also link to "\A\B",
                    // instead of "\Z\B".
                    //
                    // In reality, I doubt that ANY user would EVER have such a
                    // weird inheritance structure, with "extends" across
                    // namespaces and relative paths to classes that exist in
                    // both namespaces, etc. And the problem above would not be
                    // able to happen as long as their "Z\Z" map has declared
                    // ANY other value too (or even just changed the order of
                    // the declarations), so that we detect that their constant
                    // differs in "Z\Z". So 99.9999% of users will be safe.
                    //
                    // And no... we CAN'T simply always re-interpret it, because
                    // then we'd get a FAR more dangerous bug, which means that
                    // ALL relative properties would re-compile every time they
                    // are inherited, "as if they had been declared by us".
                    //
                    // So no, this method really is the BEST that PHP < 7.1 has.
                    $hasDifferentConstant = ($rawClassPropertyMap
                                             !== $this->_previousMapConstantValue);

                    // Update the "previous constant value" since we will be the
                    // new "previous" value after this iteration.
                    $this->_previousMapConstantValue = $rawClassPropertyMap;
                }
            }
            if (!$foundConstant) {
                // The constant doesn't exist. Should never be able to happen
                // since the class inherits from LazyJsonMapper.
                throw new ReflectionException(
                    // NOTE: This exception message mimics PHP's own reflection
                    // error message style.
                    'Constant JSON_PROPERTY_MAP does not exist'
                );
            }
        } catch (ReflectionException $e) {
            // Unable to read the map constant from the class...
            throw new BadPropertyMapException(sprintf(
                'Reflection of JSON_PROPERTY_MAP constant failed for class "%s". Reason: "%s".',
                $this->_currentClassInfo['strictClassName'], $e->getMessage()
            ));
        }

        // If we've already parsed that class before, it means that we've
        // already fully parsed it AND its parents. If so, just use the cache.
        if (isset($this->_propertyMapCache->classMaps[$this->_currentClassInfo['className']])) {
            // Overwrite our whole "_currentClassPropertyMap" with the one from
            // our parent instead. That's faster than merging the arrays, and
            // guarantees PERFECT PropertyDefinition instance re-usage.
            // NOTE: To explain it, imagine class B extending from A. A has
            // already been parsed and cached. We then instantiate B for the
            // first time and we need to build B's definitions. By copying the
            // cache from A (its parent), we'll re-use all of A's finished
            // PropertyDefinition objects in B and throughout any other chain
            // that derives from the same base object (A). This holds true for
            // ALL objects that inherit (or "import") ANYTHING (such as
            // something that later refers to "B"), since we ALWAYS resolve the
            // chains top-down from the base class to the deepest child class,
            // and therefore cache the base (hierarchically shared) definitions
            // FIRST.
            $this->_currentClassPropertyMap = $this->_propertyMapCache->classMaps[
                $this->_currentClassInfo['className']
            ];
        } else {
            // We have not parsed/encountered this class before...

            // Only parse its class constant if it differs from its inherited
            // parent's constant value. Otherwise we'll keep the parent's map
            // (the value that's currently in "_currentClassPropertyMap").
            if ($hasDifferentConstant) {
                // Process and validate the JSON_PROPERTY_MAP of the class.
                if (!is_array($rawClassPropertyMap)) {
                    throw new BadPropertyMapException(sprintf(
                        'Invalid JSON property map in class "%s". The map must be an array.',
                        $this->_currentClassInfo['strictClassName']
                    ));
                }
                foreach ($rawClassPropertyMap as $propName => $propDefStr) {
                    // Process the current entry and add it to the current
                    // class' compiled property map if the entry is new/diff.
                    $this->_processPropertyMapEntry( // Throws.
                        $propName,
                        $propDefStr
                    );
                }
            }

            // Mark the fact that we have compiled this class, so that we'll be
            // able to know which classes we have compiled later. This list will
            // bubble through the compile-call-hierarchy as-needed to keep track
            // of EVERY compiled class during the current root compile()-run.
            $this->compiledClasses[$this->_currentClassInfo['className']] = true;

            // Now cache the final property-map for this particular class in the
            // inheritance chain... Note that if it had no new/different map
            // declarations of its own (despite having a different constant),
            // then we're still simply re-using the exact property-map objects
            // of its parent class here again.
            $this->_propertyMapCache->classMaps[
                $this->_currentClassInfo['className']
            ] = $this->_currentClassPropertyMap;
        } // End of class-map "lookup-or-compilation".

        // IMPORTANT: We've finished processing a class. If this was one of the
        // classes that WE locked, we MUST NOW unlock it, but ONLY if we
        // successfully processed the class (added it to the cache). Otherwise
        // we'll unlock it at the end (where we unlock any stragglers) instead.
        // NOTE: We AREN'T allowed to unlock classes we didn't lock.
        // NOTE: Unlocking is IMPORTANT, since it's necessary for being able to
        // import classes from diverging branches with shared inheritance
        // ancestors. If we don't release the parent-classes one by one as we
        // resolve them, then we would never be able to import classes from
        // other branches of our parent/class hierarchy, since the imports would
        // see that one of their parents (from our hierarchy) is still locked
        // and would refuse to compile themselves.
        if (isset($this->_ourCompilerClassLocks[$this->_currentClassInfo['className']])) {
            unset($this->_ourCompilerClassLocks[$this->_currentClassInfo['className']]);
            unset($this->_propertyMapCache->compilerLocks[$this->_currentClassInfo['className']]);
        }
    }
private function _processCurrentClass ( ) { // We must always begin by reflecting its "JSON_PROPERTY_MAP" class // constant to determine if this particular class in the hierarchy // re-defines its value (compared to inheriting it from its "extends" // parent). This protects against accidentally re-parsing inherited // constants, which would both waste time AND would be VERY dangerous, // since that would re-interpret all inherited relative properties (ones // pointing at classes relative to the class which ACTUALLY declared // that constant) as if they were relative to the INHERITING class // instead, which is VERY wrong (and could lead to classes being either // mismapped or "not found"). Luckily the code below fully protects us // against that scenario, by detecting if our value is "ours" or not. try { // Use different techniques based on what their PHP supports. $ foundConstant = false ; if ( version_compare ( PHP_VERSION , '7.1.0' ) >= 0 ) { // In PHP 7.1.0 and higher, they've finally added "reflection // constants" which allow us to get accurate extra information. $ reflectionConstant = $ this -> _currentClassInfo [ 'reflector' ] -> getReflectionConstant ( 'JSON_PROPERTY_MAP' ) ; if ( $ reflectionConstant !== false ) { $ foundConstant = true ; // Just read its value. We don't have to care about its // isPrivate() flags etc. It lets us read the value anyway. $ rawClassPropertyMap = $ reflectionConstant -> getValue ( ) ; // Use PHP7.1's ReflectionClassConstant's ability to tell us // EXACTLY which class declared the current (inherited or // new) value for the constant. If OUR class didn't declare // it, then we know that it was inherited and should NOT be // parsed again. But if we DID declare its value, then we // know that we MUST parse it ("has different constant"). // NOTE: This method is 100% accurate even when re-declared // to the exact same value as the inherited (parent) value! $ hasDifferentConstant = ( $ reflectionConstant -> getDeclaringClass ( ) -> getName ( ) === $ this -> _currentClassInfo [ 'className' ] ) ; } } else { // In older PHP versions, we're pretty limited... we don't get // ANY extra information about the constants. We just get their // values. And if we try to query a specific constant, we get // FALSE if it doesn't exist (which is indistinguishable from it // actually having FALSE values). So we MUST get an array of all // constants to be able to check whether it TRULY exists or not. $ classConstants = $ this -> _currentClassInfo [ 'reflector' ] -> getConstants ( ) ; if ( array_key_exists ( 'JSON_PROPERTY_MAP' , $ classConstants ) ) { $ foundConstant = true ; // Read its value. Unfortunately old versions of PHP don't // give us ANY information about which class declared it // (meaning whether it was inherited or re-declared here). $ rawClassPropertyMap = $ classConstants [ 'JSON_PROPERTY_MAP' ] ; // MAGIC: This is the best we can do on old PHP versions... // The "!==" ensures that this constant really differs from // its parent. In case of arrays, they are only treated as // identical if both arrays have the same key & value types // and values in the exact same order and same count(). And // it checks recursively! // // NOTE: This method is great, but it's not as accurate as // the perfect PHP 7.1 method. It actually has a VERY TINY // issue where it will fail to detect a new constant: If you // manually re-declare a constant to EXACTLY the same value // as what you would have inherited from your parent // ("extends") hierarchy, then we won't be able to detect // that you have a new value. Instead, you will inherit the // compiled parent class map as if you hadn't declared any // value of your own at all. In almost all cases, that won't // matter, and will give you the same result. It only // matters in a very tiny case which probably won't happen // to anybody in real-world usage: // // namespace A { class A { "foo":"B[]" } class B {} } // namespace Z { class Z extends \A\A { "foo":"B[]" } class B {} } // // In that situation, Z\Z would inherit the constant from // A\A, which compiled the relative-class "foo" property as // "\A\B". And when we look at Z's own constant, we would // see a 100% identical JSON_PROPERTY_MAP constant compared // to A\A, so we would assume that since all the array keys // and values match exactly, its constant "was inherited". // Therefore the "identical" constant of Z will not be // parsed. And Z's foo will therefore also link to "\A\B", // instead of "\Z\B". // // In reality, I doubt that ANY user would EVER have such a // weird inheritance structure, with "extends" across // namespaces and relative paths to classes that exist in // both namespaces, etc. And the problem above would not be // able to happen as long as their "Z\Z" map has declared // ANY other value too (or even just changed the order of // the declarations), so that we detect that their constant // differs in "Z\Z". So 99.9999% of users will be safe. // // And no... we CAN'T simply always re-interpret it, because // then we'd get a FAR more dangerous bug, which means that // ALL relative properties would re-compile every time they // are inherited, "as if they had been declared by us". // // So no, this method really is the BEST that PHP < 7.1 has. $ hasDifferentConstant = ( $ rawClassPropertyMap !== $ this -> _previousMapConstantValue ) ; // Update the "previous constant value" since we will be the // new "previous" value after this iteration. $ this -> _previousMapConstantValue = $ rawClassPropertyMap ; } } if ( ! $ foundConstant ) { // The constant doesn't exist. Should never be able to happen // since the class inherits from LazyJsonMapper. throw new ReflectionException ( // NOTE: This exception message mimics PHP's own reflection // error message style. 'Constant JSON_PROPERTY_MAP does not exist' ) ; } } catch ( ReflectionException $ e ) { // Unable to read the map constant from the class... throw new BadPropertyMapException ( sprintf ( 'Reflection of JSON_PROPERTY_MAP constant failed for class "%s". Reason: "%s".' , $ this -> _currentClassInfo [ 'strictClassName' ] , $ e -> getMessage ( ) ) ) ; } // If we've already parsed that class before, it means that we've // already fully parsed it AND its parents. If so, just use the cache. if ( isset ( $ this -> _propertyMapCache -> classMaps [ $ this -> _currentClassInfo [ 'className' ] ] ) ) { // Overwrite our whole "_currentClassPropertyMap" with the one from // our parent instead. That's faster than merging the arrays, and // guarantees PERFECT PropertyDefinition instance re-usage. // NOTE: To explain it, imagine class B extending from A. A has // already been parsed and cached. We then instantiate B for the // first time and we need to build B's definitions. By copying the // cache from A (its parent), we'll re-use all of A's finished // PropertyDefinition objects in B and throughout any other chain // that derives from the same base object (A). This holds true for // ALL objects that inherit (or "import") ANYTHING (such as // something that later refers to "B"), since we ALWAYS resolve the // chains top-down from the base class to the deepest child class, // and therefore cache the base (hierarchically shared) definitions // FIRST. $ this -> _currentClassPropertyMap = $ this -> _propertyMapCache -> classMaps [ $ this -> _currentClassInfo [ 'className' ] ] ; } else { // We have not parsed/encountered this class before... // Only parse its class constant if it differs from its inherited // parent's constant value. Otherwise we'll keep the parent's map // (the value that's currently in "_currentClassPropertyMap"). if ( $ hasDifferentConstant ) { // Process and validate the JSON_PROPERTY_MAP of the class. if ( ! is_array ( $ rawClassPropertyMap ) ) { throw new BadPropertyMapException ( sprintf ( 'Invalid JSON property map in class "%s". The map must be an array.' , $ this -> _currentClassInfo [ 'strictClassName' ] ) ) ; } foreach ( $ rawClassPropertyMap as $ propName => $ propDefStr ) { // Process the current entry and add it to the current // class' compiled property map if the entry is new/diff. $ this -> _processPropertyMapEntry ( // Throws. $ propName , $ propDefStr ) ; } } // Mark the fact that we have compiled this class, so that we'll be // able to know which classes we have compiled later. This list will // bubble through the compile-call-hierarchy as-needed to keep track // of EVERY compiled class during the current root compile()-run. $ this -> compiledClasses [ $ this -> _currentClassInfo [ 'className' ] ] = true ; // Now cache the final property-map for this particular class in the // inheritance chain... Note that if it had no new/different map // declarations of its own (despite having a different constant), // then we're still simply re-using the exact property-map objects // of its parent class here again. $ this -> _propertyMapCache -> classMaps [ $ this -> _currentClassInfo [ 'className' ] ] = $ this -> _currentClassPropertyMap ; } // End of class-map "lookup-or-compilation". // IMPORTANT: We've finished processing a class. If this was one of the // classes that WE locked, we MUST NOW unlock it, but ONLY if we // successfully processed the class (added it to the cache). Otherwise // we'll unlock it at the end (where we unlock any stragglers) instead. // NOTE: We AREN'T allowed to unlock classes we didn't lock. // NOTE: Unlocking is IMPORTANT, since it's necessary for being able to // import classes from diverging branches with shared inheritance // ancestors. If we don't release the parent-classes one by one as we // resolve them, then we would never be able to import classes from // other branches of our parent/class hierarchy, since the imports would // see that one of their parents (from our hierarchy) is still locked // and would refuse to compile themselves. if ( isset ( $ this -> _ourCompilerClassLocks [ $ this -> _currentClassInfo [ 'className' ] ] ) ) { unset ( $ this -> _ourCompilerClassLocks [ $ this -> _currentClassInfo [ 'className' ] ] ) ; unset ( $ this -> _propertyMapCache -> compilerLocks [ $ this -> _currentClassInfo [ 'className' ] ] ) ; } }
Process the current class in the solve-class hierarchy.

@throws BadPropertyDefinitionException
@throws BadPropertyMapException
@throws CircularPropertyMapException
Process the current class in the solve - class hierarchy .