private function _importClassMap(
        $strictImportClassName)
    {
        // Begin via reflection to resolve the EXACT name of the class they want
        // to import, so that we can trust its name completely.
        try {
            // NOTE: The strict, global "\"-name is necessary here, to guarantee
            // that we find the correct target class via the global namespace.
            $reflector = new ReflectionClass($strictImportClassName);
            $importClassName = $reflector->getName(); // The clean, real name.
            $strictImportClassName = Utilities::createStrictClassPath($importClassName);
        } catch (ReflectionException $e) {
            // This should never be able to fail (since our caller already
            // verified that the class exists), but treat failure as a bad map.
            throw new BadPropertyMapException(sprintf(
                'Reflection failed for class "%s", when trying to import it into class "%s". Reason: "%s".',
                $strictImportClassName, $this->_currentClassInfo['strictClassName'], $e->getMessage()
            ));
        }

        // FATAL: If the encountered import-statement literally refers to itself
        // (the class we're currently compiling in the hierarchy), then it's a
        // totally ridiculous circular self-reference of the most insane kind.
        if ($importClassName === $this->_currentClassInfo['className']) {
            throw new CircularPropertyMapException(
                $strictImportClassName,
                $strictImportClassName
            );
        }

        // FATAL: If the import-statement refers to ANY class in OUR OWN
        // current class' hierarchy, then it's a circular self-reference. We
        // forbid those because they make ZERO sense. For example: "ABCD", in
        // that case, "D" COULD definitely import "B" because "D" is later in
        // the chain and "B" is therefore already resolved. But WHY? We already
        // inherit from it! And we could NEVER do the reverse; "B" could never
        // import "D", because "D" depends on "B" being fully compiled already.
        // Likewise, we can't allow stupid things like "B imports B". Doesn't
        // make sense. Therefore, we disallow ALL self-imports.
        //
        // NOTE: In the example of "B" importing "D", that's NOT caught here
        // since the classname "D" wouldn't be part of B's hierarchy. But it
        // will be caught during our upcoming attempt to sub-compile "D" to
        // parse ITS hierarchy, since "D" will begin to compile and check its
        // own class hierarchy, and will arrive at the "B" class and will then
        // detect that "B" is locked as "already being resolved", since the
        // ongoing resolving of "B" was what triggered the compilation of "D".
        if (isset($this->_classHierarchy[$importClassName])) {
            // NOTE: strictSolveClassName goes in "arg1" because we're the ones
            // trying to import an invalid target class that's already part of
            // our own hierarchy.
            throw new CircularPropertyMapException(
                $this->_strictSolveClassName,
                $strictImportClassName
            );
        }

        // FATAL: Also prevent users from importing any class that's in the list
        // of "locked and currently being resolved", since that means that the
        // class they're trying to import is ITSELF part of a compilation-chain
        // that's CURRENTLY trying to resolve itself right now.
        //
        // NOTE: This catches scenarios where class-chains have common ancestors
        // but then diverge and go into a circular reference to each other. For
        // example, "A extends LazyJsonMapper, imports B", "B extends
        // LazyJsonMapper, imports A". Imagine that you construct "A", which
        // locks "A" during its compilation process. The parent "LazyJsonMapper"
        // class is successfully compiled since it has no dependencies. Then "A"
        // sees "import B" and sub-compiles B. (Remember that "A" remains locked
        // when that happens, since "A" is waiting for "B" to resolve itself.)
        // The compilation code of "B" begins running. It first checks its
        // inheritance hierarchy and sees nothing locked (LazyJsonMapper is
        // unlocked because it's already done, and B is unlocked because B has
        // not locked itself yet). Next, "B" sees an "import A" statement. It
        // succeeds the "block import of A if it's part of our own hierarchy"
        // check above, since B is NOT "extended from A".
        //
        // So then we're at a crossroads... there IS a circular reference, but
        // we don't know it yet. We COULD let it proceed to "resolve A by
        // sub-compiling the map for A", which would give us a call-stack of
        // "compile A -> compile B -> compile A", but then the 2nd "A" compiler
        // would run, and would do its early lock-check and notice that itself
        // (A) is locked, since the initial compilation of "A" is not yet done.
        // It would then throw a non-sensical error saying that there's a
        // circular reference between "A and A", which is technically true, but
        // makes no sense. That's because we waited too long!
        //
        // So, what the code BELOW does instead, is that when "B" is trying to
        // resolve itself (during the "compile A -> import B -> compile B"
        // phase), the "B" compiler will come across its "import A" statement,
        // and will now detect that "A" is already locked, and therefore throws
        // a proper exception now instead of letting "A" attempt to compile
        // itself a second time as described above. The result of this early
        // sanity-check is a better exception message (correct class names).
        foreach ($this->_propertyMapCache->compilerLocks as $lockedClassName => $isLocked) {
            if ($isLocked && $lockedClassName === $importClassName) {
                // NOTE: strictSolveClassName goes in "arg2" because we're
                // trying to import something unresolved that's therefore higher
                // than us in the call-stack.
                throw new CircularPropertyMapException(
                    $strictImportClassName,
                    $this->_strictSolveClassName
                );
            }
        }

        // Now check if the target class is missing from our cache, and if so
        // then we must attempt to compile (and cache) the map of the target
        // class that we have been told to import.
        //
        // NOTE: This sub-compilation will perform the necessary recursive
        // compilation and validation of the import's WHOLE map hierarchy. When
        // the compiler runs, it will ensure that nothing in the target class
        // hierarchy is currently locked (that would be a circular reference),
        // and then it will lock its own hierarchy too until it has resolved
        // itself. This will go on recursively if necessary, until ALL parents
        // and ALL further imports have been resolved. And if there are ANY
        // circular reference ANYWHERE, or ANY other problems with its
        // map/definitions, then the sub-compilation(s) will throw appropriate
        // exceptions.
        //
        // We won't catch them; we'll just let them bubble up to abort the
        // entire chain of compilation, since a single error anywhere in the
        // whole chain means that whatever root-level compile-call initially
        // began this compilation process _cannot_ be resolved either. So we'll
        // let the error bubble up all the way to the original call.
        if (!isset($this->_propertyMapCache->classMaps[$importClassName])) {
            // Compile the target class to import.
            // NOTE: We will not catch the error. So if the import fails to
            // compile, it'll simply output its own message saying that
            // something was wrong in THAT class. We don't bother including any
            // info about our class (the one which imported it). That's still
            // very clear in this case, since the top-level class compiler and
            // its hierarchy will be in the stack trace as well as always having
            // a clearly visible import-command in their class property map.
            $this->_subcompile($importClassName); // Throws.
        }

        // Now simply loop through the compiled property map of the imported
        // class... and add every property to our own compiled property map. In
        // case of clashes, we use the imported one.
        // NOTE: We'll directly assign its PD objects as-is, which will assign
        // the objects by shallow "shared instance", so that we re-use the
        // PropertyDefinition object instances from the compiled class that
        // we're importing. That will save memory.
        // NOTE: There's no point doing an equals() clash-check here, since
        // we're importing an EXISTING, necessary definition from another class,
        // so we won't save any memory by avoiding their version even if the
        // definitions are equal.
        foreach ($this->_propertyMapCache->classMaps[$importClassName] as $importedPropName => $importedPropDefObj) {
            $this->_currentClassPropertyMap[$importedPropName] = $importedPropDefObj;
        }
    }
private function _importClassMap(
        $strictImportClassName)
    {
        // Begin via reflection to resolve the EXACT name of the class they want
        // to import, so that we can trust its name completely.
        try {
            // NOTE: The strict, global "\"-name is necessary here, to guarantee
            // that we find the correct target class via the global namespace.
            $reflector = new ReflectionClass($strictImportClassName);
            $importClassName = $reflector->getName(); // The clean, real name.
            $strictImportClassName = Utilities::createStrictClassPath($importClassName);
        } catch (ReflectionException $e) {
            // This should never be able to fail (since our caller already
            // verified that the class exists), but treat failure as a bad map.
            throw new BadPropertyMapException(sprintf(
                'Reflection failed for class "%s", when trying to import it into class "%s". Reason: "%s".',
                $strictImportClassName, $this->_currentClassInfo['strictClassName'], $e->getMessage()
            ));
        }

        // FATAL: If the encountered import-statement literally refers to itself
        // (the class we're currently compiling in the hierarchy), then it's a
        // totally ridiculous circular self-reference of the most insane kind.
        if ($importClassName === $this->_currentClassInfo['className']) {
            throw new CircularPropertyMapException(
                $strictImportClassName,
                $strictImportClassName
            );
        }

        // FATAL: If the import-statement refers to ANY class in OUR OWN
        // current class' hierarchy, then it's a circular self-reference. We
        // forbid those because they make ZERO sense. For example: "ABCD", in
        // that case, "D" COULD definitely import "B" because "D" is later in
        // the chain and "B" is therefore already resolved. But WHY? We already
        // inherit from it! And we could NEVER do the reverse; "B" could never
        // import "D", because "D" depends on "B" being fully compiled already.
        // Likewise, we can't allow stupid things like "B imports B". Doesn't
        // make sense. Therefore, we disallow ALL self-imports.
        //
        // NOTE: In the example of "B" importing "D", that's NOT caught here
        // since the classname "D" wouldn't be part of B's hierarchy. But it
        // will be caught during our upcoming attempt to sub-compile "D" to
        // parse ITS hierarchy, since "D" will begin to compile and check its
        // own class hierarchy, and will arrive at the "B" class and will then
        // detect that "B" is locked as "already being resolved", since the
        // ongoing resolving of "B" was what triggered the compilation of "D".
        if (isset($this->_classHierarchy[$importClassName])) {
            // NOTE: strictSolveClassName goes in "arg1" because we're the ones
            // trying to import an invalid target class that's already part of
            // our own hierarchy.
            throw new CircularPropertyMapException(
                $this->_strictSolveClassName,
                $strictImportClassName
            );
        }

        // FATAL: Also prevent users from importing any class that's in the list
        // of "locked and currently being resolved", since that means that the
        // class they're trying to import is ITSELF part of a compilation-chain
        // that's CURRENTLY trying to resolve itself right now.
        //
        // NOTE: This catches scenarios where class-chains have common ancestors
        // but then diverge and go into a circular reference to each other. For
        // example, "A extends LazyJsonMapper, imports B", "B extends
        // LazyJsonMapper, imports A". Imagine that you construct "A", which
        // locks "A" during its compilation process. The parent "LazyJsonMapper"
        // class is successfully compiled since it has no dependencies. Then "A"
        // sees "import B" and sub-compiles B. (Remember that "A" remains locked
        // when that happens, since "A" is waiting for "B" to resolve itself.)
        // The compilation code of "B" begins running. It first checks its
        // inheritance hierarchy and sees nothing locked (LazyJsonMapper is
        // unlocked because it's already done, and B is unlocked because B has
        // not locked itself yet). Next, "B" sees an "import A" statement. It
        // succeeds the "block import of A if it's part of our own hierarchy"
        // check above, since B is NOT "extended from A".
        //
        // So then we're at a crossroads... there IS a circular reference, but
        // we don't know it yet. We COULD let it proceed to "resolve A by
        // sub-compiling the map for A", which would give us a call-stack of
        // "compile A -> compile B -> compile A", but then the 2nd "A" compiler
        // would run, and would do its early lock-check and notice that itself
        // (A) is locked, since the initial compilation of "A" is not yet done.
        // It would then throw a non-sensical error saying that there's a
        // circular reference between "A and A", which is technically true, but
        // makes no sense. That's because we waited too long!
        //
        // So, what the code BELOW does instead, is that when "B" is trying to
        // resolve itself (during the "compile A -> import B -> compile B"
        // phase), the "B" compiler will come across its "import A" statement,
        // and will now detect that "A" is already locked, and therefore throws
        // a proper exception now instead of letting "A" attempt to compile
        // itself a second time as described above. The result of this early
        // sanity-check is a better exception message (correct class names).
        foreach ($this->_propertyMapCache->compilerLocks as $lockedClassName => $isLocked) {
            if ($isLocked && $lockedClassName === $importClassName) {
                // NOTE: strictSolveClassName goes in "arg2" because we're
                // trying to import something unresolved that's therefore higher
                // than us in the call-stack.
                throw new CircularPropertyMapException(
                    $strictImportClassName,
                    $this->_strictSolveClassName
                );
            }
        }

        // Now check if the target class is missing from our cache, and if so
        // then we must attempt to compile (and cache) the map of the target
        // class that we have been told to import.
        //
        // NOTE: This sub-compilation will perform the necessary recursive
        // compilation and validation of the import's WHOLE map hierarchy. When
        // the compiler runs, it will ensure that nothing in the target class
        // hierarchy is currently locked (that would be a circular reference),
        // and then it will lock its own hierarchy too until it has resolved
        // itself. This will go on recursively if necessary, until ALL parents
        // and ALL further imports have been resolved. And if there are ANY
        // circular reference ANYWHERE, or ANY other problems with its
        // map/definitions, then the sub-compilation(s) will throw appropriate
        // exceptions.
        //
        // We won't catch them; we'll just let them bubble up to abort the
        // entire chain of compilation, since a single error anywhere in the
        // whole chain means that whatever root-level compile-call initially
        // began this compilation process _cannot_ be resolved either. So we'll
        // let the error bubble up all the way to the original call.
        if (!isset($this->_propertyMapCache->classMaps[$importClassName])) {
            // Compile the target class to import.
            // NOTE: We will not catch the error. So if the import fails to
            // compile, it'll simply output its own message saying that
            // something was wrong in THAT class. We don't bother including any
            // info about our class (the one which imported it). That's still
            // very clear in this case, since the top-level class compiler and
            // its hierarchy will be in the stack trace as well as always having
            // a clearly visible import-command in their class property map.
            $this->_subcompile($importClassName); // Throws.
        }

        // Now simply loop through the compiled property map of the imported
        // class... and add every property to our own compiled property map. In
        // case of clashes, we use the imported one.
        // NOTE: We'll directly assign its PD objects as-is, which will assign
        // the objects by shallow "shared instance", so that we re-use the
        // PropertyDefinition object instances from the compiled class that
        // we're importing. That will save memory.
        // NOTE: There's no point doing an equals() clash-check here, since
        // we're importing an EXISTING, necessary definition from another class,
        // so we won't save any memory by avoiding their version even if the
        // definitions are equal.
        foreach ($this->_propertyMapCache->classMaps[$importClassName] as $importedPropName => $importedPropDefObj) {
            $this->_currentClassPropertyMap[$importedPropName] = $importedPropDefObj;
        }
    }
private function _importClassMap ( $ strictImportClassName ) { // Begin via reflection to resolve the EXACT name of the class they want // to import, so that we can trust its name completely. try { // NOTE: The strict, global "\"-name is necessary here, to guarantee // that we find the correct target class via the global namespace. $ reflector = new ReflectionClass ( $ strictImportClassName ) ; $ importClassName = $ reflector -> getName ( ) ; // The clean, real name. $ strictImportClassName = Utilities :: createStrictClassPath ( $ importClassName ) ; } catch ( ReflectionException $ e ) { // This should never be able to fail (since our caller already // verified that the class exists), but treat failure as a bad map. throw new BadPropertyMapException ( sprintf ( 'Reflection failed for class "%s", when trying to import it into class "%s". Reason: "%s".' , $ strictImportClassName , $ this -> _currentClassInfo [ 'strictClassName' ] , $ e -> getMessage ( ) ) ) ; } // FATAL: If the encountered import-statement literally refers to itself // (the class we're currently compiling in the hierarchy), then it's a // totally ridiculous circular self-reference of the most insane kind. if ( $ importClassName === $ this -> _currentClassInfo [ 'className' ] ) { throw new CircularPropertyMapException ( $ strictImportClassName , $ strictImportClassName ) ; } // FATAL: If the import-statement refers to ANY class in OUR OWN // current class' hierarchy, then it's a circular self-reference. We // forbid those because they make ZERO sense. For example: "ABCD", in // that case, "D" COULD definitely import "B" because "D" is later in // the chain and "B" is therefore already resolved. But WHY? We already // inherit from it! And we could NEVER do the reverse; "B" could never // import "D", because "D" depends on "B" being fully compiled already. // Likewise, we can't allow stupid things like "B imports B". Doesn't // make sense. Therefore, we disallow ALL self-imports. // // NOTE: In the example of "B" importing "D", that's NOT caught here // since the classname "D" wouldn't be part of B's hierarchy. But it // will be caught during our upcoming attempt to sub-compile "D" to // parse ITS hierarchy, since "D" will begin to compile and check its // own class hierarchy, and will arrive at the "B" class and will then // detect that "B" is locked as "already being resolved", since the // ongoing resolving of "B" was what triggered the compilation of "D". if ( isset ( $ this -> _classHierarchy [ $ importClassName ] ) ) { // NOTE: strictSolveClassName goes in "arg1" because we're the ones // trying to import an invalid target class that's already part of // our own hierarchy. throw new CircularPropertyMapException ( $ this -> _strictSolveClassName , $ strictImportClassName ) ; } // FATAL: Also prevent users from importing any class that's in the list // of "locked and currently being resolved", since that means that the // class they're trying to import is ITSELF part of a compilation-chain // that's CURRENTLY trying to resolve itself right now. // // NOTE: This catches scenarios where class-chains have common ancestors // but then diverge and go into a circular reference to each other. For // example, "A extends LazyJsonMapper, imports B", "B extends // LazyJsonMapper, imports A". Imagine that you construct "A", which // locks "A" during its compilation process. The parent "LazyJsonMapper" // class is successfully compiled since it has no dependencies. Then "A" // sees "import B" and sub-compiles B. (Remember that "A" remains locked // when that happens, since "A" is waiting for "B" to resolve itself.) // The compilation code of "B" begins running. It first checks its // inheritance hierarchy and sees nothing locked (LazyJsonMapper is // unlocked because it's already done, and B is unlocked because B has // not locked itself yet). Next, "B" sees an "import A" statement. It // succeeds the "block import of A if it's part of our own hierarchy" // check above, since B is NOT "extended from A". // // So then we're at a crossroads... there IS a circular reference, but // we don't know it yet. We COULD let it proceed to "resolve A by // sub-compiling the map for A", which would give us a call-stack of // "compile A -> compile B -> compile A", but then the 2nd "A" compiler // would run, and would do its early lock-check and notice that itself // (A) is locked, since the initial compilation of "A" is not yet done. // It would then throw a non-sensical error saying that there's a // circular reference between "A and A", which is technically true, but // makes no sense. That's because we waited too long! // // So, what the code BELOW does instead, is that when "B" is trying to // resolve itself (during the "compile A -> import B -> compile B" // phase), the "B" compiler will come across its "import A" statement, // and will now detect that "A" is already locked, and therefore throws // a proper exception now instead of letting "A" attempt to compile // itself a second time as described above. The result of this early // sanity-check is a better exception message (correct class names). foreach ( $ this -> _propertyMapCache -> compilerLocks as $ lockedClassName => $ isLocked ) { if ( $ isLocked && $ lockedClassName === $ importClassName ) { // NOTE: strictSolveClassName goes in "arg2" because we're // trying to import something unresolved that's therefore higher // than us in the call-stack. throw new CircularPropertyMapException ( $ strictImportClassName , $ this -> _strictSolveClassName ) ; } } // Now check if the target class is missing from our cache, and if so // then we must attempt to compile (and cache) the map of the target // class that we have been told to import. // // NOTE: This sub-compilation will perform the necessary recursive // compilation and validation of the import's WHOLE map hierarchy. When // the compiler runs, it will ensure that nothing in the target class // hierarchy is currently locked (that would be a circular reference), // and then it will lock its own hierarchy too until it has resolved // itself. This will go on recursively if necessary, until ALL parents // and ALL further imports have been resolved. And if there are ANY // circular reference ANYWHERE, or ANY other problems with its // map/definitions, then the sub-compilation(s) will throw appropriate // exceptions. // // We won't catch them; we'll just let them bubble up to abort the // entire chain of compilation, since a single error anywhere in the // whole chain means that whatever root-level compile-call initially // began this compilation process _cannot_ be resolved either. So we'll // let the error bubble up all the way to the original call. if ( ! isset ( $ this -> _propertyMapCache -> classMaps [ $ importClassName ] ) ) { // Compile the target class to import. // NOTE: We will not catch the error. So if the import fails to // compile, it'll simply output its own message saying that // something was wrong in THAT class. We don't bother including any // info about our class (the one which imported it). That's still // very clear in this case, since the top-level class compiler and // its hierarchy will be in the stack trace as well as always having // a clearly visible import-command in their class property map. $ this -> _subcompile ( $ importClassName ) ; // Throws. } // Now simply loop through the compiled property map of the imported // class... and add every property to our own compiled property map. In // case of clashes, we use the imported one. // NOTE: We'll directly assign its PD objects as-is, which will assign // the objects by shallow "shared instance", so that we re-use the // PropertyDefinition object instances from the compiled class that // we're importing. That will save memory. // NOTE: There's no point doing an equals() clash-check here, since // we're importing an EXISTING, necessary definition from another class, // so we won't save any memory by avoiding their version even if the // definitions are equal. foreach ( $ this -> _propertyMapCache -> classMaps [ $ importClassName ] as $ importedPropName => $ importedPropDefObj ) { $ this -> _currentClassPropertyMap [ $ importedPropName ] = $ importedPropDefObj ; } }
Import another class map into the current class.

@param string $strictImportClassName The strict, global path to the
class, with leading backslash `\`.
To save time, we assume the caller
has already verified that it is a
valid `LazyJsonMapper` class.

@throws BadPropertyDefinitionException
@throws BadPropertyMapException
@throws CircularPropertyMapException
Import another class map into the current class .