mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge branch '4' into 5
This commit is contained in:
commit
fa75a36267
@ -232,9 +232,11 @@ class Convert
|
||||
* @param SimpleXMLElement $xml
|
||||
*
|
||||
* @return mixed
|
||||
* @deprecated 4.11.0 Will be removed without equivalent functionality
|
||||
*/
|
||||
protected static function recursiveXMLToArray($xml)
|
||||
{
|
||||
Deprecation::notice('4.11.0', 'Will be removed without equivalent functionality');
|
||||
$x = null;
|
||||
if ($xml instanceof SimpleXMLElement) {
|
||||
$attributes = $xml->attributes();
|
||||
|
@ -141,16 +141,23 @@ class Deprecation
|
||||
if (!self::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
$outputMessages = [];
|
||||
// using a while loop with array_shift() to ensure that self::$userErrorMessageBuffer will have
|
||||
// have values removed from it before calling user_error()
|
||||
while (count(self::$userErrorMessageBuffer)) {
|
||||
$arr = array_shift(self::$userErrorMessageBuffer);
|
||||
$message = $arr['message'];
|
||||
// often the same deprecation message appears dozens of times, which isn't helpful
|
||||
// only need to show a single instance of each message
|
||||
if (in_array($message, $outputMessages)) {
|
||||
continue;
|
||||
}
|
||||
$calledInsideWithNoReplacement = $arr['calledInsideWithNoReplacement'];
|
||||
if ($calledInsideWithNoReplacement && !self::$showNoReplacementNotices) {
|
||||
continue;
|
||||
}
|
||||
user_error($message, E_USER_DEPRECATED);
|
||||
$outputMessages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,9 +215,10 @@ class FixtureTestState implements TestState
|
||||
*/
|
||||
protected function getTestAbsolutePath(SapphireTest $test)
|
||||
{
|
||||
$filename = ClassLoader::inst()->getItemPath(get_class($test));
|
||||
$class = get_class($test);
|
||||
$filename = ClassLoader::inst()->getItemPath($class);
|
||||
if (!$filename) {
|
||||
throw new LogicException('getItemPath returned null for ' . static::class
|
||||
throw new LogicException('getItemPath returned null for ' . $class
|
||||
. '. Try adding flush=1 to the test run.');
|
||||
}
|
||||
return dirname($filename ?? '');
|
||||
|
@ -162,7 +162,7 @@ class StandardRelatedDataService implements RelatedDataService
|
||||
$tableName = $this->dataObjectSchema->tableName($candidateClass);
|
||||
break;
|
||||
}
|
||||
$candidateClass = get_parent_class($class ?? '');
|
||||
$candidateClass = get_parent_class($candidateClass ?? '');
|
||||
}
|
||||
return $tableName;
|
||||
}
|
||||
|
@ -548,9 +548,10 @@ class i18nTextCollector
|
||||
*/
|
||||
public function collectFromCode($content, $fileName, Module $module)
|
||||
{
|
||||
// Get namespace either from $fileName or $module fallback
|
||||
// Get "namespace" either from $fileName or $module fallback
|
||||
$namespace = $fileName ? basename($fileName) : $module->getName();
|
||||
|
||||
$usedFQCNs = [];
|
||||
$entities = [];
|
||||
|
||||
$tokens = token_get_all("<?php\n" . $content);
|
||||
@ -558,12 +559,17 @@ class i18nTextCollector
|
||||
$inConcat = false;
|
||||
$inNamespace = false;
|
||||
$inClass = false; // after `class` but before `{`
|
||||
$inUse = false; // pulling in classes from other namespaces
|
||||
$inArrayClosedBy = false; // Set to the expected closing token, or false if not in array
|
||||
$inSelf = false; // Tracks progress of collecting self::class
|
||||
$currentEntity = [];
|
||||
$currentNameSpace = []; // The actual namespace for the current class
|
||||
$currentClass = []; // Class components
|
||||
$previousToken = null;
|
||||
$thisToken = null; // used to populate $previousToken on next iteration
|
||||
$potentialClassName = null;
|
||||
$currentUse = null;
|
||||
$currentUseAlias = null;
|
||||
foreach ($tokens as $token) {
|
||||
// Shuffle last token to $lastToken
|
||||
$previousToken = $thisToken;
|
||||
@ -571,17 +577,54 @@ class i18nTextCollector
|
||||
if (is_array($token)) {
|
||||
list($id, $text) = $token;
|
||||
|
||||
// Collect use statements so we can get fully qualified class names
|
||||
if ($id === T_USE) {
|
||||
$inUse = true;
|
||||
$currentUse = [];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($inUse) {
|
||||
// PHP 8.0+
|
||||
if (defined('T_NAME_QUALIFIED') && $id === T_NAME_QUALIFIED) {
|
||||
$currentUse[] = $text;
|
||||
$text = explode('\\', $text);
|
||||
$currentUseAlias = end($text);
|
||||
continue;
|
||||
}
|
||||
// PHP 7.4 or an alias declaration
|
||||
if ($id === T_STRING) {
|
||||
// Only add to the FQCN if it's the first string or comes after a namespace separator
|
||||
if (empty($currentUse) || (is_array($previousToken) && $previousToken[0] === T_NS_SEPARATOR)) {
|
||||
$currentUse[] = $text;
|
||||
}
|
||||
// The last part of the use statement is always the alias or the actual class name
|
||||
$currentUseAlias = $text;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check class
|
||||
if ($id === T_NAMESPACE) {
|
||||
$inNamespace = true;
|
||||
$currentClass = [];
|
||||
$currentNameSpace = [];
|
||||
continue;
|
||||
}
|
||||
if ($inNamespace && ($id === T_STRING || (defined('T_NAME_QUALIFIED') && $id === T_NAME_QUALIFIED))) {
|
||||
$currentClass[] = $text;
|
||||
$currentNameSpace[] = $text;
|
||||
continue;
|
||||
}
|
||||
|
||||
// This could be a ClassName::class declaration
|
||||
if ($id === T_DOUBLE_COLON && is_array($previousToken) && $previousToken[0] === T_STRING) {
|
||||
$prevString = $previousToken[1];
|
||||
if (!in_array($prevString, ['self', 'static', 'parent'])) {
|
||||
$potentialClassName = $prevString;
|
||||
}
|
||||
}
|
||||
|
||||
// Check class
|
||||
if ($id === T_CLASS) {
|
||||
// Skip if previous token was '::'. E.g. 'Object::class'
|
||||
@ -591,6 +634,16 @@ class i18nTextCollector
|
||||
// for __CLASS__ to handle an array of class parts
|
||||
$id = T_CLASS_C;
|
||||
$inSelf = false;
|
||||
} elseif ($potentialClassName) {
|
||||
$id = T_CONSTANT_ENCAPSED_STRING;
|
||||
if (array_key_exists($potentialClassName, $usedFQCNs)) {
|
||||
// Handle classes that we explicitly know about from use statements
|
||||
$text = "'" . $usedFQCNs[$potentialClassName] . "'";
|
||||
} else {
|
||||
// Assume the class is in the current namespace
|
||||
$potentialFQCN = [...$currentNameSpace, $potentialClassName];
|
||||
$text = "'" . implode('\\', $potentialFQCN) . "'";
|
||||
}
|
||||
} else {
|
||||
// Don't handle other ::class definitions. We can't determine which
|
||||
// class was invoked, so parent::class is not possible at this point.
|
||||
@ -600,7 +653,11 @@ class i18nTextCollector
|
||||
$inClass = true;
|
||||
continue;
|
||||
}
|
||||
} elseif (is_array($previousToken) && $previousToken[0] === T_DOUBLE_COLON) {
|
||||
// We had a potential class but it turns out it was probably a method call.
|
||||
$potentialClassName = null;
|
||||
}
|
||||
|
||||
if ($inClass && $id === T_STRING) {
|
||||
$currentClass[] = $text;
|
||||
$inClass = false;
|
||||
@ -691,10 +748,19 @@ class i18nTextCollector
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we can close the namespace
|
||||
if ($inNamespace && $token === ';') {
|
||||
$inNamespace = false;
|
||||
continue;
|
||||
// Check if we can close the namespace or use statement
|
||||
if ($token === ';') {
|
||||
if ($inNamespace) {
|
||||
$inNamespace = false;
|
||||
continue;
|
||||
}
|
||||
if ($inUse) {
|
||||
$inUse = false;
|
||||
$usedFQCNs[$currentUseAlias] = implode('\\', $currentUse);
|
||||
$currentUse = null;
|
||||
$currentUseAlias = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Continue only if in translation and not in array
|
||||
|
@ -19,6 +19,9 @@ class MonologErrorHandlerTest extends SapphireTest
|
||||
|
||||
public function testSetLoggerResetsStack()
|
||||
{
|
||||
if (Deprecation::isEnabled()) {
|
||||
$this->markTestSkipped('Test calls deprecated code');
|
||||
}
|
||||
/** @var LoggerInterface $logger */
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
|
@ -7,6 +7,7 @@ use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\PasswordEncryptor;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\Security\DefaultAdminService;
|
||||
use SilverStripe\Security\Security;
|
||||
|
||||
class SecurityDefaultAdminTest extends SapphireTest
|
||||
{
|
||||
@ -35,6 +36,7 @@ class SecurityDefaultAdminTest extends SapphireTest
|
||||
$this->defaultUsername = null;
|
||||
$this->defaultPassword = null;
|
||||
}
|
||||
Security::config()->set('password_encryption_algorithm', 'blowfish');
|
||||
DefaultAdminService::setDefaultAdmin('admin', 'password');
|
||||
Permission::reset();
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ use SilverStripe\View\Requirements_Backend;
|
||||
use SilverStripe\Core\Manifest\ResourceURLGenerator;
|
||||
use SilverStripe\Control\SimpleResourceURLGenerator;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Dev\Deprecation;
|
||||
use SilverStripe\View\SSViewer;
|
||||
use SilverStripe\View\ThemeResourceLoader;
|
||||
|
||||
|
@ -348,6 +348,64 @@ PHP;
|
||||
);
|
||||
}
|
||||
|
||||
public function testCollectFromClassSyntax()
|
||||
{
|
||||
$c = i18nTextCollector::create();
|
||||
$mymodule = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
||||
$php = <<<PHP
|
||||
<?php
|
||||
namespace SilverStripe\Framework\Core;
|
||||
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
use Some\Space\MyClass as AliasClass;
|
||||
use NoNamespaceClass;
|
||||
|
||||
class MyClass extends Base implements SomeService {
|
||||
public function pointlessFunction1(\$class) {
|
||||
if (
|
||||
!is_subclass_of(\$class, DataObject::class)
|
||||
|| !Object::has_extension(\$class, Versioned::class)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return _t(
|
||||
Versioned::class . '.OTHER_NAMESPACE',
|
||||
'New Lines'
|
||||
);
|
||||
}
|
||||
public function pointlessFunction2() {
|
||||
return _t(
|
||||
SameNamespaceClass::class . '.SAME_NAMESPACE',
|
||||
'Slash=\\\\, Quote=\\''
|
||||
);
|
||||
}
|
||||
public function pointlessFunction3() {
|
||||
return _t(
|
||||
AliasClass::class . ".ALIAS_CLASS",
|
||||
"Slash=\\\\, Quote=\\""
|
||||
);
|
||||
}
|
||||
public function pointlessFunction4()
|
||||
{
|
||||
return _t(
|
||||
NoNamespaceClass::class . '.NO_NAMESPACE',
|
||||
'Self Class'
|
||||
);
|
||||
}
|
||||
}
|
||||
PHP;
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
'SilverStripe\\Versioned\\Versioned.OTHER_NAMESPACE' => "New Lines",
|
||||
'SilverStripe\\Framework\\Core\\SameNamespaceClass.SAME_NAMESPACE' => 'Slash=\\, Quote=\'',
|
||||
'Some\\Space\\MyClass.ALIAS_CLASS' => 'Slash=\\, Quote="',
|
||||
'NoNamespaceClass.NO_NAMESPACE' => 'Self Class',
|
||||
],
|
||||
$c->collectFromCode($php, null, $mymodule)
|
||||
);
|
||||
}
|
||||
|
||||
public function testNewlinesInEntityValues()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user