Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F5991011
ContextFactory.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
14 KB
Referenced Files
None
Subscribers
None
ContextFactory.php
View Options
<?php
declare
(
strict_types
=
1
);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/
namespace
phpDocumentor\Reflection\Types
;
use
ArrayIterator
;
use
InvalidArgumentException
;
use
ReflectionClass
;
use
ReflectionClassConstant
;
use
ReflectionMethod
;
use
ReflectionParameter
;
use
ReflectionProperty
;
use
Reflector
;
use
RuntimeException
;
use
UnexpectedValueException
;
use
function
define
;
use
function
defined
;
use
function
file_exists
;
use
function
file_get_contents
;
use
function
get_class
;
use
function
in_array
;
use
function
is_string
;
use
function
strrpos
;
use
function
substr
;
use
function
token_get_all
;
use
function
trim
;
use
const
T_AS
;
use
const
T_CLASS
;
use
const
T_CURLY_OPEN
;
use
const
T_DOLLAR_OPEN_CURLY_BRACES
;
use
const
T_NAME_FULLY_QUALIFIED
;
use
const
T_NAME_QUALIFIED
;
use
const
T_NAMESPACE
;
use
const
T_NS_SEPARATOR
;
use
const
T_STRING
;
use
const
T_TRAIT
;
use
const
T_USE
;
if
(!
defined
(
'T_NAME_QUALIFIED'
))
{
define
(
'T_NAME_QUALIFIED'
,
10001
);
}
if
(!
defined
(
'T_NAME_FULLY_QUALIFIED'
))
{
define
(
'T_NAME_FULLY_QUALIFIED'
,
10002
);
}
/**
* Convenience class to create a Context for DocBlocks when not using the Reflection Component of phpDocumentor.
*
* For a DocBlock to be able to resolve types that use partial namespace names or rely on namespace imports we need to
* provide a bit of context so that the DocBlock can read that and based on it decide how to resolve the types to
* Fully Qualified names.
*
* @see Context for more information.
*/
final
class
ContextFactory
{
/** The literal used at the end of a use statement. */
private
const
T_LITERAL_END_OF_USE
=
';'
;
/** The literal used between sets of use statements */
private
const
T_LITERAL_USE_SEPARATOR
=
','
;
/**
* Build a Context given a Class Reflection.
*
* @see Context for more information on Contexts.
*/
public
function
createFromReflector
(
Reflector
$reflector
):
Context
{
if
(
$reflector
instanceof
ReflectionClass
)
{
//phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable
/** @var ReflectionClass<object> $reflector */
return
$this
->
createFromReflectionClass
(
$reflector
);
}
if
(
$reflector
instanceof
ReflectionParameter
)
{
return
$this
->
createFromReflectionParameter
(
$reflector
);
}
if
(
$reflector
instanceof
ReflectionMethod
)
{
return
$this
->
createFromReflectionMethod
(
$reflector
);
}
if
(
$reflector
instanceof
ReflectionProperty
)
{
return
$this
->
createFromReflectionProperty
(
$reflector
);
}
if
(
$reflector
instanceof
ReflectionClassConstant
)
{
return
$this
->
createFromReflectionClassConstant
(
$reflector
);
}
throw
new
UnexpectedValueException
(
'Unhandled
\R
eflector instance given: '
.
get_class
(
$reflector
));
}
private
function
createFromReflectionParameter
(
ReflectionParameter
$parameter
):
Context
{
$class
=
$parameter
->
getDeclaringClass
();
if
(!
$class
)
{
throw
new
InvalidArgumentException
(
'Unable to get class of '
.
$parameter
->
getName
());
}
return
$this
->
createFromReflectionClass
(
$class
);
}
private
function
createFromReflectionMethod
(
ReflectionMethod
$method
):
Context
{
$class
=
$method
->
getDeclaringClass
();
return
$this
->
createFromReflectionClass
(
$class
);
}
private
function
createFromReflectionProperty
(
ReflectionProperty
$property
):
Context
{
$class
=
$property
->
getDeclaringClass
();
return
$this
->
createFromReflectionClass
(
$class
);
}
private
function
createFromReflectionClassConstant
(
ReflectionClassConstant
$constant
):
Context
{
//phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable
/** @phpstan-var ReflectionClass<object> $class */
$class
=
$constant
->
getDeclaringClass
();
return
$this
->
createFromReflectionClass
(
$class
);
}
/**
* @phpstan-param ReflectionClass<object> $class
*/
private
function
createFromReflectionClass
(
ReflectionClass
$class
):
Context
{
$fileName
=
$class
->
getFileName
();
$namespace
=
$class
->
getNamespaceName
();
if
(
is_string
(
$fileName
)
&&
file_exists
(
$fileName
))
{
$contents
=
file_get_contents
(
$fileName
);
if
(
$contents
===
false
)
{
throw
new
RuntimeException
(
'Unable to read file "'
.
$fileName
.
'"'
);
}
return
$this
->
createForNamespace
(
$namespace
,
$contents
);
}
return
new
Context
(
$namespace
,
[]);
}
/**
* Build a Context for a namespace in the provided file contents.
*
* @see Context for more information on Contexts.
*
* @param string $namespace It does not matter if a `\` precedes the namespace name,
* this method first normalizes.
* @param string $fileContents The file's contents to retrieve the aliases from with the given namespace.
*/
public
function
createForNamespace
(
string
$namespace
,
string
$fileContents
):
Context
{
$namespace
=
trim
(
$namespace
,
'
\\
'
);
$useStatements
=
[];
$currentNamespace
=
''
;
$tokens
=
new
ArrayIterator
(
token_get_all
(
$fileContents
));
while
(
$tokens
->
valid
())
{
$currentToken
=
$tokens
->
current
();
switch
(
$currentToken
[
0
])
{
case
T_NAMESPACE
:
$currentNamespace
=
$this
->
parseNamespace
(
$tokens
);
break
;
case
T_CLASS
:
case
T_TRAIT
:
// Fast-forward the iterator through the class so that any
// T_USE tokens found within are skipped - these are not
// valid namespace use statements so should be ignored.
$braceLevel
=
0
;
$firstBraceFound
=
false
;
while
(
$tokens
->
valid
()
&&
(
$braceLevel
>
0
||
!
$firstBraceFound
))
{
$currentToken
=
$tokens
->
current
();
if
(
$currentToken
===
'{'
||
in_array
(
$currentToken
[
0
],
[
T_CURLY_OPEN
,
T_DOLLAR_OPEN_CURLY_BRACES
],
true
)
)
{
if
(!
$firstBraceFound
)
{
$firstBraceFound
=
true
;
}
++
$braceLevel
;
}
if
(
$currentToken
===
'}'
)
{
--
$braceLevel
;
}
$tokens
->
next
();
}
break
;
case
T_USE
:
if
(
$currentNamespace
===
$namespace
)
{
$useStatements
+=
$this
->
parseUseStatement
(
$tokens
);
}
break
;
}
$tokens
->
next
();
}
return
new
Context
(
$namespace
,
$useStatements
);
}
/**
* Deduce the name from tokens when we are at the T_NAMESPACE token.
*
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
*/
private
function
parseNamespace
(
ArrayIterator
$tokens
):
string
{
// skip to the first string or namespace separator
$this
->
skipToNextStringOrNamespaceSeparator
(
$tokens
);
$name
=
''
;
$acceptedTokens
=
[
T_STRING
,
T_NS_SEPARATOR
,
T_NAME_QUALIFIED
];
while
(
$tokens
->
valid
()
&&
in_array
(
$tokens
->
current
()[
0
],
$acceptedTokens
,
true
))
{
$name
.=
$tokens
->
current
()[
1
];
$tokens
->
next
();
}
return
$name
;
}
/**
* Deduce the names of all imports when we are at the T_USE token.
*
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
*
* @return string[]
* @psalm-return array<string, string>
*/
private
function
parseUseStatement
(
ArrayIterator
$tokens
):
array
{
$uses
=
[];
while
(
$tokens
->
valid
())
{
$this
->
skipToNextStringOrNamespaceSeparator
(
$tokens
);
$uses
+=
$this
->
extractUseStatements
(
$tokens
);
$currentToken
=
$tokens
->
current
();
if
(
$currentToken
[
0
]
===
self
::
T_LITERAL_END_OF_USE
)
{
return
$uses
;
}
}
return
$uses
;
}
/**
* Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token.
*
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
*/
private
function
skipToNextStringOrNamespaceSeparator
(
ArrayIterator
$tokens
):
void
{
while
(
$tokens
->
valid
())
{
$currentToken
=
$tokens
->
current
();
if
(
in_array
(
$currentToken
[
0
],
[
T_STRING
,
T_NS_SEPARATOR
],
true
))
{
break
;
}
if
(
$currentToken
[
0
]
===
T_NAME_QUALIFIED
)
{
break
;
}
if
(
defined
(
'T_NAME_FULLY_QUALIFIED'
)
&&
$currentToken
[
0
]
===
T_NAME_FULLY_QUALIFIED
)
{
break
;
}
$tokens
->
next
();
}
}
/**
* Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of
* a USE statement yet. This will return a key/value array of the alias => namespace.
*
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
*
* @return string[]
* @psalm-return array<string, string>
*
* @psalm-suppress TypeDoesNotContainType
*/
private
function
extractUseStatements
(
ArrayIterator
$tokens
):
array
{
$extractedUseStatements
=
[];
$groupedNs
=
''
;
$currentNs
=
''
;
$currentAlias
=
''
;
$state
=
'start'
;
while
(
$tokens
->
valid
())
{
$currentToken
=
$tokens
->
current
();
$tokenId
=
is_string
(
$currentToken
)
?
$currentToken
:
$currentToken
[
0
];
$tokenValue
=
is_string
(
$currentToken
)
?
null
:
$currentToken
[
1
];
switch
(
$state
)
{
case
'start'
:
switch
(
$tokenId
)
{
case
T_STRING
:
case
T_NS_SEPARATOR
:
$currentNs
.=
(
string
)
$tokenValue
;
$currentAlias
=
$tokenValue
;
break
;
case
T_NAME_QUALIFIED
:
case
T_NAME_FULLY_QUALIFIED
:
$currentNs
.=
(
string
)
$tokenValue
;
$currentAlias
=
substr
(
(
string
)
$tokenValue
,
(
int
)
(
strrpos
((
string
)
$tokenValue
,
'
\\
'
))
+
1
);
break
;
case
T_CURLY_OPEN
:
case
'{'
:
$state
=
'grouped'
;
$groupedNs
=
$currentNs
;
break
;
case
T_AS
:
$state
=
'start-alias'
;
break
;
case
self
::
T_LITERAL_USE_SEPARATOR
:
case
self
::
T_LITERAL_END_OF_USE
:
$state
=
'end'
;
break
;
default
:
break
;
}
break
;
case
'start-alias'
:
switch
(
$tokenId
)
{
case
T_STRING
:
$currentAlias
=
$tokenValue
;
break
;
case
self
::
T_LITERAL_USE_SEPARATOR
:
case
self
::
T_LITERAL_END_OF_USE
:
$state
=
'end'
;
break
;
default
:
break
;
}
break
;
case
'grouped'
:
switch
(
$tokenId
)
{
case
T_STRING
:
case
T_NS_SEPARATOR
:
$currentNs
.=
(
string
)
$tokenValue
;
$currentAlias
=
$tokenValue
;
break
;
case
T_AS
:
$state
=
'grouped-alias'
;
break
;
case
self
::
T_LITERAL_USE_SEPARATOR
:
$state
=
'grouped'
;
$extractedUseStatements
[(
string
)
$currentAlias
]
=
$currentNs
;
$currentNs
=
$groupedNs
;
$currentAlias
=
''
;
break
;
case
self
::
T_LITERAL_END_OF_USE
:
$state
=
'end'
;
break
;
default
:
break
;
}
break
;
case
'grouped-alias'
:
switch
(
$tokenId
)
{
case
T_STRING
:
$currentAlias
=
$tokenValue
;
break
;
case
self
::
T_LITERAL_USE_SEPARATOR
:
$state
=
'grouped'
;
$extractedUseStatements
[(
string
)
$currentAlias
]
=
$currentNs
;
$currentNs
=
$groupedNs
;
$currentAlias
=
''
;
break
;
case
self
::
T_LITERAL_END_OF_USE
:
$state
=
'end'
;
break
;
default
:
break
;
}
}
if
(
$state
===
'end'
)
{
break
;
}
$tokens
->
next
();
}
if
(
$groupedNs
!==
$currentNs
)
{
$extractedUseStatements
[(
string
)
$currentAlias
]
=
$currentNs
;
}
return
$extractedUseStatements
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Oct 4 2026, 01:43 (4 w, 3 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
71/d7/1e0707093a4dfe55a5c0b8c59e15
Default Alt Text
ContextFactory.php (14 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment