Page MenuHomePhabricator

D8915.diff
No OneTemporary

D8915.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -231,6 +231,7 @@
'PhutilPayPalAPIFuture' => 'future/paypal/PhutilPayPalAPIFuture.php',
'PhutilPerson' => 'internationalization/PhutilPerson.php',
'PhutilPersonTest' => 'internationalization/__tests__/PhutilPersonTest.php',
+ 'PhutilPhobjectTestCase' => 'object/__tests__/PhutilPhobjectTestCase.php',
'PhutilProcessGroupDaemon' => 'daemon/torture/PhutilProcessGroupDaemon.php',
'PhutilProtocolChannel' => 'channel/PhutilProtocolChannel.php',
'PhutilProxyException' => 'error/PhutilProxyException.php',
@@ -291,6 +292,7 @@
'PhutilSyntaxHighlighterEngine' => 'markup/syntax/engine/PhutilSyntaxHighlighterEngine.php',
'PhutilSyntaxHighlighterException' => 'markup/syntax/highlighter/PhutilSyntaxHighlighterException.php',
'PhutilTestCase' => 'infrastructure/testing/PhutilTestCase.php',
+ 'PhutilTestPhobject' => 'object/__tests__/PhutilTestPhobject.php',
'PhutilTortureTestDaemon' => 'daemon/torture/PhutilTortureTestDaemon.php',
'PhutilTranslator' => 'internationalization/PhutilTranslator.php',
'PhutilTranslatorTestCase' => 'internationalization/__tests__/PhutilTranslatorTestCase.php',
@@ -489,6 +491,7 @@
'PHPASTParserTestCase' => 'PhutilTestCase',
'PhageAgentTestCase' => 'PhutilTestCase',
'PhagePHPAgentBootloader' => 'PhageAgentBootloader',
+ 'Phobject' => 'Iterator',
'PhutilAWSEC2Future' => 'PhutilAWSFuture',
'PhutilAWSException' => 'Exception',
'PhutilAWSFuture' => 'FutureProxy',
@@ -607,6 +610,7 @@
'PhutilParserGeneratorUnreachableTerminalException' => 'PhutilParserGeneratorException',
'PhutilPayPalAPIFuture' => 'FutureProxy',
'PhutilPersonTest' => 'PhutilPerson',
+ 'PhutilPhobjectTestCase' => 'PhutilTestCase',
'PhutilProcessGroupDaemon' => 'PhutilTortureTestDaemon',
'PhutilProtocolChannel' => 'PhutilChannelChannel',
'PhutilProxyException' => 'Exception',
@@ -649,6 +653,7 @@
'PhutilSocketChannel' => 'PhutilChannel',
'PhutilSyntaxHighlighterException' => 'Exception',
'PhutilTestCase' => 'ArcanistPhutilTestCase',
+ 'PhutilTestPhobject' => 'Phobject',
'PhutilTortureTestDaemon' => 'PhutilDaemon',
'PhutilTranslatorTestCase' => 'PhutilTestCase',
'PhutilTwitchFuture' => 'FutureProxy',
diff --git a/src/object/Phobject.php b/src/object/Phobject.php
--- a/src/object/Phobject.php
+++ b/src/object/Phobject.php
@@ -1,12 +1,58 @@
<?php
-abstract class Phobject {
+/**
+ * Base class for libphutil objects. Enforces stricter object semantics than
+ * PHP.
+ *
+ * When a program attempts to write to an undeclared object property, PHP
+ * creates the property. However, in libphutil this is always an error (for
+ * example, a misspelled property name). Instead of permitting the write,
+ * subclasses will throw when an undeclared property is written.
+ *
+ * When a program attempts to iterate an object (for example, with `foreach`),
+ * PHP iterates its public members. However, in libphutil this is always an
+ * error (for example, iterating over the wrong variable). Instead of
+ * permitting the iteration, subclasses will throw when an object is iterated.
+ *
+ * (Legitimately iterable subclasses can provide a working implementation of
+ * Iterator instead.)
+ */
+abstract class Phobject implements Iterator {
public function __set($name, $value) {
throw new DomainException(
- "Attempted write to undeclared property ".get_class($this)."::\$$name. ".
- "This is an application error, please report it at ".
- "https://secure.phabricator.com/maniphest/task/create/");
+ pht(
+ 'Attempt to write to undeclared property %s::%s.',
+ get_class($this),
+ $name));
+ }
+
+ public function current() {
+ $this->throwOnAttemptedIteration();
+ }
+
+ public function key() {
+ $this->throwOnAttemptedIteration();
+ }
+
+ public function next() {
+ $this->throwOnAttemptedIteration();
+ }
+
+ public function rewind() {
+ $this->throwOnAttemptedIteration();
+ }
+
+ public function valid() {
+ $this->throwOnAttemptedIteration();
+ }
+
+ private function throwOnAttemptedIteration() {
+ throw new DomainException(
+ pht(
+ 'Attempting to iterate an object (of class %s) which is not '.
+ 'iterable.',
+ get_class($this)));
}
}
diff --git a/src/object/__tests__/PhutilPhobjectTestCase.php b/src/object/__tests__/PhutilPhobjectTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/object/__tests__/PhutilPhobjectTestCase.php
@@ -0,0 +1,33 @@
+<?php
+
+final class PhutilPhobjectTestCase extends PhutilTestCase {
+
+ public function testThrowOnUndeclaredProperty() {
+ $object = new PhutilTestPhobject();
+
+ $caught = null;
+ try {
+ $object->duck = 'quack';
+ } catch (Exception $ex) {
+ $caught = $ex;
+ }
+
+ $this->assertTrue($caught instanceof DomainException);
+ }
+
+ public function testThrowOnIteration() {
+ $object = new PhutilTestPhobject();
+
+ $caught = null;
+ try {
+ foreach ($object as $item) {
+ // ...
+ }
+ } catch (Exception $ex) {
+ $caught = $ex;
+ }
+
+ $this->assertTrue($caught instanceof DomainException);
+ }
+
+}
diff --git a/src/object/__tests__/PhutilTestPhobject.php b/src/object/__tests__/PhutilTestPhobject.php
new file mode 100644
--- /dev/null
+++ b/src/object/__tests__/PhutilTestPhobject.php
@@ -0,0 +1,5 @@
+<?php
+
+final class PhutilTestPhobject extends Phobject {
+
+}

File Metadata

Mime Type
text/plain
Expires
Sun, May 12, 5:53 AM (3 w, 11 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6290369
Default Alt Text
D8915.diff (5 KB)

Event Timeline