ENH PHP 8.2 support

This commit is contained in:
Steve Boyd 2022-12-21 14:44:47 +13:00
parent bf440e42e7
commit c1a773310d
24 changed files with 131 additions and 73 deletions

View File

@ -27,7 +27,7 @@
"composer/installers": "^2.2", "composer/installers": "^2.2",
"guzzlehttp/guzzle": "^7.5.0", "guzzlehttp/guzzle": "^7.5.0",
"guzzlehttp/psr7": "^2.4.0", "guzzlehttp/psr7": "^2.4.0",
"embed/embed": "^4.4.4", "embed/embed": "^4.4.7",
"league/csv": "^9.8.0", "league/csv": "^9.8.0",
"m1/env": "^2.2.0", "m1/env": "^2.2.0",
"monolog/monolog": "^3.2.0", "monolog/monolog": "^3.2.0",

View File

@ -579,7 +579,7 @@ class HTTPRequest implements ArrayAccess
$shiftCount = sizeof($patternParts ?? []); $shiftCount = sizeof($patternParts ?? []);
$remaining = count($this->dirParts ?? []) - $i; $remaining = count($this->dirParts ?? []) - $i;
for ($j = 1; $j <= $remaining; $j++) { for ($j = 1; $j <= $remaining; $j++) {
$arguments["$${j}"] = $this->dirParts[$j + $i - 1]; $arguments['$' . $j] = $this->dirParts[$j + $i - 1];
} }
$patternParts = array_merge($patternParts, array_keys($arguments ?? [])); $patternParts = array_merge($patternParts, array_keys($arguments ?? []));
break; break;

View File

@ -20,6 +20,8 @@ class SSListContains extends Constraint implements TestOnly
*/ */
protected $matches = []; protected $matches = [];
protected SSListExporter $exporter;
/** /**
* Check if the list has left over items that don't match * Check if the list has left over items that don't match
* *

View File

@ -19,6 +19,8 @@ class SSListContainsOnlyMatchingItems extends Constraint implements TestOnly
*/ */
private $match; private $match;
protected SSListExporter $exporter;
/** /**
* @var ViewableDataContains * @var ViewableDataContains
*/ */

View File

@ -34,6 +34,8 @@ class FixtureBlueprint
*/ */
protected $class; protected $class;
private FixtureFactory $factory;
/** /**
* @var array * @var array
*/ */
@ -70,6 +72,17 @@ class FixtureBlueprint
$this->defaults = $defaults; $this->defaults = $defaults;
} }
public function getFactory(): FixtureFactory
{
return $this->factory;
}
public function setFactory(FixtureFactory $factory): static
{
$this->factory = $factory;
return $this;
}
/** /**
* @param string $identifier Unique identifier for this fixture type * @param string $identifier Unique identifier for this fixture type
* @param array $data Map of property names to their values. * @param array $data Map of property names to their values.

View File

@ -161,6 +161,8 @@ abstract class SapphireTest extends TestCase implements TestOnly
*/ */
protected static $tempDB = null; protected static $tempDB = null;
protected FixtureFactory|bool $fixtureFactory;
/** /**
* @return TempDatabase * @return TempDatabase
*/ */

View File

@ -65,6 +65,8 @@ class MySQLDatabase extends Database implements TransactionManager
*/ */
private $transactionManager = null; private $transactionManager = null;
private int $transactionNesting = 0;
/** /**
* Default collation * Default collation
* *

View File

@ -73,7 +73,6 @@ class MySQLStatement extends Query
public function __destruct() public function __destruct()
{ {
$this->statement->close(); $this->statement->close();
$this->currentRecord = false;
} }
/** /**

View File

@ -6,6 +6,7 @@ use BadMethodCallException;
use Exception; use Exception;
use InvalidArgumentException; use InvalidArgumentException;
use LogicException; use LogicException;
use SilverStripe\Assets\Storage\DBFile;
use SilverStripe\Core\ClassInfo; use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Injector\Injector;
@ -2841,13 +2842,21 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
// later referenced to update the parent dataobject // later referenced to update the parent dataobject
if ($val instanceof DBComposite) { if ($val instanceof DBComposite) {
$val->bindTo($this); $val->bindTo($this);
$this->record[$fieldName] = $val; $this->setFieldValue($fieldName, $val);
} }
// Situation 2: Passing a literal or non-DBField object // Situation 2: Passing a literal or non-DBField object
} else { } else {
$this->setFieldValue($fieldName, $val);
}
return $this;
}
private function setFieldValue(string $fieldName, mixed $val): void
{
$schema = static::getSchema();
// If this is a proper database field, we shouldn't be getting non-DBField objects // If this is a proper database field, we shouldn't be getting non-DBField objects
if (is_object($val) && $schema->fieldSpec(static::class, $fieldName)) { if (is_object($val) && !($val instanceof DBField) && $schema->fieldSpec(static::class, $fieldName)) {
throw new InvalidArgumentException('DataObject::setField: passed an object that is not a DBField'); throw new InvalidArgumentException('DataObject::setFieldValue: passed an object that is not a DBField');
} }
if (!empty($val) && !is_scalar($val)) { if (!empty($val) && !is_scalar($val)) {
@ -2855,7 +2864,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if ($dbField && $dbField->scalarValueOnly()) { if ($dbField && $dbField->scalarValueOnly()) {
throw new InvalidArgumentException( throw new InvalidArgumentException(
sprintf( sprintf(
'DataObject::setField: %s only accepts scalars', 'DataObject::setFieldValue: %s only accepts scalars',
$fieldName $fieldName
) )
); );
@ -2883,8 +2892,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
// Value is saved regardless, since the change detection relates to the last write // Value is saved regardless, since the change detection relates to the last write
$this->record[$fieldName] = $val; $this->record[$fieldName] = $val;
} }
return $this;
}
/** /**
* Set the value of the field, using a casting object. * Set the value of the field, using a casting object.

View File

@ -46,7 +46,7 @@ class DBBoolean extends DBField
{ {
$fieldName = $this->name; $fieldName = $this->name;
if ($fieldName) { if ($fieldName) {
$dataObject->$fieldName = ($this->value) ? 1 : 0; $dataObject->setField($fieldName, $this->value ? 1 : 0);
} else { } else {
$class = static::class; $class = static::class;
throw new \RuntimeException("DBField::saveInto() Called on a nameless '$class' object"); throw new \RuntimeException("DBField::saveInto() Called on a nameless '$class' object");

View File

@ -88,7 +88,8 @@ class DBDecimal extends DBField
$fieldName = $this->name; $fieldName = $this->name;
if ($fieldName) { if ($fieldName) {
$dataObject->$fieldName = (float)preg_replace('/[^0-9.\-\+]/', '', $this->value ?? ''); $value = (float) preg_replace('/[^0-9.\-\+]/', '', $this->value ?? '');
$dataObject->setField($fieldName, $value);
} else { } else {
throw new \UnexpectedValueException( throw new \UnexpectedValueException(
"DBField::saveInto() Called on a nameless '" . static::class . "' object" "DBField::saveInto() Called on a nameless '" . static::class . "' object"

View File

@ -542,7 +542,7 @@ abstract class DBField extends ViewableData implements DBIndexable
"DBField::saveInto() Called on a nameless '" . static::class . "' object" "DBField::saveInto() Called on a nameless '" . static::class . "' object"
); );
} }
$dataObject->$fieldName = $this->value; $dataObject->setField($fieldName, $this->value);
} }
/** /**

View File

@ -45,7 +45,7 @@ class DBPercentage extends DBDecimal
$fieldName = $this->name; $fieldName = $this->name;
if ($fieldName && $dataObject->$fieldName > 1.0) { if ($fieldName && $dataObject->$fieldName > 1.0) {
$dataObject->$fieldName = 1.0; $dataObject->setField($fieldName, 1.0);
} }
} }
} }

View File

@ -66,6 +66,11 @@ class ViewableData implements IteratorAggregate
*/ */
private static $casting_cache = []; private static $casting_cache = [];
/**
* Acts as a PHP 8.2+ compliant replacement for dynamic properties
*/
private array $data = [];
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
/** /**
@ -191,7 +196,7 @@ class ViewableData implements IteratorAggregate
*/ */
public function hasField($field) public function hasField($field)
{ {
return property_exists($this, $field ?? ''); return property_exists($this, $field) || isset($this->data[$field]);
} }
/** /**
@ -202,8 +207,11 @@ class ViewableData implements IteratorAggregate
*/ */
public function getField($field) public function getField($field)
{ {
if (property_exists($this, $field)) {
return $this->$field; return $this->$field;
} }
return $this->data[$field];
}
/** /**
* Set a field on this object. This should be overloaded in child classes. * Set a field on this object. This should be overloaded in child classes.
@ -215,7 +223,13 @@ class ViewableData implements IteratorAggregate
public function setField($field, $value) public function setField($field, $value)
{ {
$this->objCacheClear(); $this->objCacheClear();
// prior to PHP 8.2 support ViewableData::setField() simply used `$this->field = $value;`
// so the following logic essentially mimics this behaviour, though without the use
// of now deprecated dynamic properties
if (property_exists($this, $field)) {
$this->$field = $value; $this->$field = $value;
}
$this->data[$field] = $value;
return $this; return $this;
} }
@ -509,14 +523,14 @@ class ViewableData implements IteratorAggregate
* A simple wrapper around {@link ViewableData::obj()} that automatically caches the result so it can be used again * A simple wrapper around {@link ViewableData::obj()} that automatically caches the result so it can be used again
* without re-running the method. * without re-running the method.
* *
* @param string $field * @param string $fieldName
* @param array $arguments * @param array $arguments
* @param string $identifier an optional custom cache identifier * @param string $identifier an optional custom cache identifier
* @return Object|DBField * @return Object|DBField
*/ */
public function cachedCall($field, $arguments = [], $identifier = null) public function cachedCall($fieldName, $arguments = [], $identifier = null)
{ {
return $this->obj($field, $arguments, true, $identifier); return $this->obj($fieldName, $arguments, true, $identifier);
} }
/** /**

View File

@ -59,19 +59,26 @@ class ViewableData_Customised extends ViewableData
return $this->customised->hasMethod($method) || $this->original->hasMethod($method); return $this->customised->hasMethod($method) || $this->original->hasMethod($method);
} }
public function cachedCall($field, $arguments = null, $identifier = null) public function cachedCall($fieldName, $arguments = null, $identifier = null)
{ {
if ($this->customised->hasMethod($field) || $this->customised->hasField($field)) { if ($this->customisedHas($fieldName)) {
return $this->customised->cachedCall($field, $arguments, $identifier); return $this->customised->cachedCall($fieldName, $arguments, $identifier);
} }
return $this->original->cachedCall($field, $arguments, $identifier); return $this->original->cachedCall($fieldName, $arguments, $identifier);
} }
public function obj($fieldName, $arguments = null, $cache = false, $cacheName = null) public function obj($fieldName, $arguments = null, $cache = false, $cacheName = null)
{ {
if ($this->customised->hasField($fieldName) || $this->customised->hasMethod($fieldName)) { if ($this->customisedHas($fieldName)) {
return $this->customised->obj($fieldName, $arguments, $cache, $cacheName); return $this->customised->obj($fieldName, $arguments, $cache, $cacheName);
} }
return $this->original->obj($fieldName, $arguments, $cache, $cacheName); return $this->original->obj($fieldName, $arguments, $cache, $cacheName);
} }
private function customisedHas(string $fieldName): bool
{
return property_exists($this->customised, $fieldName) ||
$this->customised->hasField($fieldName) ||
$this->customised->hasMethod($fieldName);
}
} }

View File

@ -4,5 +4,7 @@ namespace SilverStripe\Core\Tests\Injector\AopProxyServiceTest;
class AnotherService class AnotherService
{ {
public $config_property;
public $filters = []; public $filters = [];
} }

View File

@ -4,6 +4,7 @@ namespace SilverStripe\Core\Tests\Injector\AopProxyServiceTest;
class SampleService class SampleService
{ {
public $auto;
public $constructorVarOne; public $constructorVarOne;
public $constructorVarTwo; public $constructorVarTwo;

View File

@ -6,6 +6,7 @@ use SilverStripe\Dev\TestOnly;
class TestObject implements TestOnly class TestObject implements TestOnly
{ {
public $auto;
public $sampleService; public $sampleService;

View File

@ -17,6 +17,8 @@ class FixtureBlueprintTest extends SapphireTest
protected $usesDatabase = true; protected $usesDatabase = true;
private int $_called = 0;
protected static $extra_dataobjects = [ protected static $extra_dataobjects = [
TestDataObject::class, TestDataObject::class,
DataObjectRelation::class, DataObjectRelation::class,

View File

@ -138,18 +138,17 @@ class GridFieldFilterHeaderTest extends SapphireTest
public function testGetSearchForm() public function testGetSearchForm()
{ {
$searchForm = $this->component->getSearchForm($this->gridField); $searchForm = $this->component->getSearchForm($this->gridField);
$this->assertTrue($searchForm instanceof Form); $this->assertTrue($searchForm instanceof Form);
$this->assertEquals('Search__q', $searchForm->fields[0]->Name); $fields = $searchForm->Fields()->toArray();
$this->assertEquals('Search__Name', $searchForm->fields[1]->Name); $this->assertEquals('Search__q', $fields[0]->Name);
$this->assertEquals('Search__City', $searchForm->fields[2]->Name); $this->assertEquals('Search__Name', $fields[1]->Name);
$this->assertEquals('Search__Cheerleader__Hat__Colour', $searchForm->fields[3]->Name); $this->assertEquals('Search__City', $fields[2]->Name);
$this->assertEquals('Search__Cheerleader__Hat__Colour', $fields[3]->Name);
$this->assertEquals('TeamsSearchForm', $searchForm->Name); $this->assertEquals('TeamsSearchForm', $searchForm->Name);
$this->assertEquals('cms-search-form', $searchForm->extraClasses['cms-search-form']); $this->assertTrue($searchForm->hasExtraClass('cms-search-form'));
foreach ($fields as $field) {
foreach ($searchForm->fields as $field) { $this->assertTrue($field->hasExtraClass('stacked'));
$this->assertEquals('stacked', $field->extraClasses['stacked']); $this->assertTrue($field->hasExtraClass('no-change-track'));
$this->assertEquals('no-change-track', $field->extraClasses['no-change-track']);
} }
} }

View File

@ -826,10 +826,6 @@ class DataObjectTest extends SapphireTest
$team1->Captain = $captain2; $team1->Captain = $captain2;
$team1->write(); $team1->write();
$this->assertEquals($captain2->ID, $team1->Captain->ID); $this->assertEquals($captain2->ID, $team1->Captain->ID);
// Setter: Custom data (required by DataDifferencer)
$team1->Captain = DBField::create_field('HTMLFragment', '<p>No captain</p>');
$this->assertEquals('<p>No captain</p>', $team1->Captain);
} }
/** /**

View File

@ -6,8 +6,9 @@ use SilverStripe\Dev\TestOnly;
class NonEmptyObject implements TestOnly class NonEmptyObject implements TestOnly
{ {
public $a;
static $c = "Cucumber"; public $b;
public static $c = "Cucumber";
public function __construct() public function __construct()
{ {

View File

@ -8,7 +8,7 @@ use Psr\Http\Message\StreamInterface;
class MockResponse implements ResponseInterface class MockResponse implements ResponseInterface
{ {
private EmbedUnitTest $unitTest; private EmbedUnitTest $unitTest;
private string $firstReponse; private string $firstResponse;
private string $secondResponse; private string $secondResponse;
public function __construct(EmbedUnitTest $unitTest, string $firstResponse, string $secondResponse) public function __construct(EmbedUnitTest $unitTest, string $firstResponse, string $secondResponse)

View File

@ -9,6 +9,13 @@
* the bracket if a failed match + restore has moved the current position backwards - so we have to check that too. * the bracket if a failed match + restore has moved the current position backwards - so we have to check that too.
*/ */
class ParserRegexp { class ParserRegexp {
public $parser;
public $rx;
public $matches;
public $match_pos;
public $check_pos;
function __construct( $parser, $rx ) { function __construct( $parser, $rx ) {
$this->parser = $parser ; $this->parser = $parser ;
$this->rx = $rx . 'Sx' ; $this->rx = $rx . 'Sx' ;