Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15479417
D8915.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
5 KB
Referenced Files
None
Subscribers
None
D8915.id.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Wed, Apr 9, 7:02 AM (2 w, 16 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7707707
Default Alt Text
D8915.id.diff (5 KB)
Attached To
Mode
D8915: Make Phobject detect iteration of non-iterable objects
Attached
Detach File
Event Timeline
Log In to Comment