Page MenuHomePhabricator

D11443.id27590.diff
No OneTemporary

D11443.id27590.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
@@ -179,6 +179,7 @@
'PhutilHangForeverDaemon' => 'daemon/torture/PhutilHangForeverDaemon.php',
'PhutilHelpArgumentWorkflow' => 'parser/argument/workflow/PhutilHelpArgumentWorkflow.php',
'PhutilHgsprintfTestCase' => 'xsprintf/__tests__/PhutilHgsprintfTestCase.php',
+ 'PhutilINIParserException' => 'parser/exception/PhutilINIParserException.php',
'PhutilIPAddress' => 'ip/PhutilIPAddress.php',
'PhutilIPAddressTestCase' => 'ip/__tests__/PhutilIPAddressTestCase.php',
'PhutilInRequestKeyValueCache' => 'cache/PhutilInRequestKeyValueCache.php',
@@ -409,6 +410,7 @@
'phutil_get_library_root_for_path' => 'moduleutils/moduleutils.php',
'phutil_get_signal_name' => 'future/exec/execx.php',
'phutil_implode_html' => 'markup/render.php',
+ 'phutil_ini_decode' => 'utils/utils.php',
'phutil_is_hiphop_runtime' => 'utils/utils.php',
'phutil_is_utf8' => 'utils/utf8.php',
'phutil_is_utf8_slowly' => 'utils/utf8.php',
@@ -604,6 +606,7 @@
'PhutilHangForeverDaemon' => 'PhutilTortureTestDaemon',
'PhutilHelpArgumentWorkflow' => 'PhutilArgumentWorkflow',
'PhutilHgsprintfTestCase' => 'PhutilTestCase',
+ 'PhutilINIParserException' => 'Exception',
'PhutilIPAddress' => 'Phobject',
'PhutilIPAddressTestCase' => 'PhutilTestCase',
'PhutilInRequestKeyValueCache' => 'PhutilKeyValueCache',
diff --git a/src/parser/exception/PhutilINIParserException.php b/src/parser/exception/PhutilINIParserException.php
new file mode 100644
--- /dev/null
+++ b/src/parser/exception/PhutilINIParserException.php
@@ -0,0 +1,3 @@
+<?php
+
+final class PhutilINIParserException extends Exception {}
diff --git a/src/utils/__tests__/PhutilUtilsTestCase.php b/src/utils/__tests__/PhutilUtilsTestCase.php
--- a/src/utils/__tests__/PhutilUtilsTestCase.php
+++ b/src/utils/__tests__/PhutilUtilsTestCase.php
@@ -535,6 +535,57 @@
}
}
+ public function testPhutilINIDecode() {
+ $valid_cases = array(
+ '' => array(),
+ 'foo=' => array('foo' => ''),
+ 'foo=bar' => array('foo' => 'bar'),
+ 'foo = bar' => array('foo' => 'bar'),
+ "foo = bar\n" => array('foo' => 'bar'),
+ "foo\nbar = baz" => array('bar' => 'baz'),
+
+ "[foo]\nbar = baz" => array('foo' => array('bar' => 'baz')),
+ "[foo]\n[bar]\nbaz = foo" => array(
+ 'foo' => array(),
+ 'bar' => array('baz' => 'foo'),
+ ),
+ "[foo]\nbar = baz\n\n[bar]\nbaz = foo" => array(
+ 'foo' => array('bar' => 'baz'),
+ 'bar' => array('baz' => 'foo'),
+ ),
+
+ "; Comment\n[foo]\nbar = baz" => array('foo' => array('bar' => 'baz')),
+ "# Comment\n[foo]\nbar = baz" => array('foo' => array('bar' => 'baz')),
+
+ "foo = true\n[bar]\nbaz = false"
+ => array('foo' => true, 'bar' => array('baz' => false)),
+ "foo = 1\nbar = 1.234" => array('foo' => 1, 'bar' => 1.234),
+ );
+
+ foreach ($valid_cases as $input => $expect) {
+ $result = phutil_ini_decode($input);
+ $this->assertEqual($expect, $result, 'phutil_ini_decode('.$input.')');
+ }
+
+ $invalid_cases = array(
+ '[' =>
+ 'syntax error, unexpected $end, expecting \']\' in Unknown on line 1',
+ "[\nfoo\n]\nbar = baz\n" =>
+ 'syntax error, unexpected $end, expecting \']\' in Unknown on line 1',
+ );
+
+ foreach ($invalid_cases as $input => $expect) {
+ $caught = null;
+ try {
+ phutil_ini_decode($input);
+ } catch (Exception $ex) {
+ $caught = $ex;
+ }
+ $this->assertTrue($caught instanceof PhutilINIParserException);
+ $this->assertEqual($expect, $caught->getMessage());
+ }
+ }
+
public function testCensorCredentials() {
$cases = array(
'' => '',
diff --git a/src/utils/utils.php b/src/utils/utils.php
--- a/src/utils/utils.php
+++ b/src/utils/utils.php
@@ -1058,6 +1058,69 @@
/**
+ * Decode an INI string.
+ *
+ * @param string
+ * @return mixed
+ */
+function phutil_ini_decode($string) {
+ $results = null;
+ $trap = new PhutilErrorTrap();
+
+ try {
+ if (!function_exists('parse_ini_string')) {
+ throw new PhutilMethodNotImplementedException(
+ pht(
+ '%s is not compatible with your version of PHP (%s). This function '.
+ 'is only supported on PHP versions newer than 5.3.0.',
+ __FUNCTION__,
+ phpversion()));
+ }
+
+ $results = @parse_ini_string($string, true, INI_SCANNER_RAW);
+
+ if ($results === false) {
+ throw new PhutilINIParserException(trim($trap->getErrorsAsString()));
+ }
+
+ foreach ($results as $section => $result) {
+ if (!is_array($result)) {
+ // We JSON decode the value in ordering to perform the following
+ // conversions:
+ //
+ // - `'true'` => `true`
+ // - `'false'` => `false`
+ // - `'123'` => `123`
+ // - `'1.234'` => `1.234`
+ //
+ $result = json_decode($result);
+
+ if ($result !== null && !is_array($result)) {
+ $results[$section] = $result;
+ }
+
+ continue;
+ }
+
+ foreach ($result as $key => $value) {
+ $value = json_decode($value);
+
+ if ($value !== null && !is_array($value)) {
+ $results[$section][$key] = $value;
+ }
+ }
+ }
+ } catch (Exception $ex) {
+ $trap->destroy();
+ throw $ex;
+ }
+
+ $trap->destroy();
+ return $results;
+}
+
+
+/**
* Attempt to censor any plaintext credentials from a string.
*
* The major use case here is to censor usernames and passwords from command

File Metadata

Mime Type
text/plain
Expires
Thu, Jun 27, 8:23 AM (1 d, 21 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6309068
Default Alt Text
D11443.id27590.diff (5 KB)

Event Timeline