diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -36,6 +36,8 @@
 
 # NPM local packages
 /support/aphlict/server/node_modules/
+/support/aphlict/server/package.json
+/support/aphlict/server/package-lock.json
 
 # Places for users to add custom resources.
 /resources/cows/custom/*
diff --git a/src/aphront/AphrontRequest.php b/src/aphront/AphrontRequest.php
--- a/src/aphront/AphrontRequest.php
+++ b/src/aphront/AphrontRequest.php
@@ -66,7 +66,7 @@
   }
 
   public static function parseURILineRange($range, $limit) {
-    if (!strlen($range)) {
+    if ($range === null || !strlen($range)) {
       return null;
     }
 
@@ -448,11 +448,10 @@
   }
 
   private function getPrefixedCookieName($name) {
-    if (strlen($this->cookiePrefix)) {
+    if ($this->cookiePrefix !== null && strlen($this->cookiePrefix)) {
       return $this->cookiePrefix.'_'.$name;
-    } else {
-      return $name;
     }
+    return $name;
   }
 
   public function getCookie($name, $default = null) {
@@ -499,7 +498,7 @@
     // domain is. This makes setup easier, and we'll tell administrators to
     // configure a base domain during the setup process.
     $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
-    if (!strlen($base_uri)) {
+    if ($base_uri === null || !strlen($base_uri)) {
       return new PhutilURI('http://'.$host.'/');
     }
 
@@ -956,7 +955,7 @@
     $submit_cookie = PhabricatorCookies::COOKIE_SUBMIT;
 
     $submit_key = $this->getCookie($submit_cookie);
-    if (strlen($submit_key)) {
+    if ($submit_key !== null && strlen($submit_key)) {
       $this->clearCookie($submit_cookie);
       $this->submitKey = $submit_key;
     }
diff --git a/src/aphront/response/AphrontFileResponse.php b/src/aphront/response/AphrontFileResponse.php
--- a/src/aphront/response/AphrontFileResponse.php
+++ b/src/aphront/response/AphrontFileResponse.php
@@ -19,7 +19,7 @@
   }
 
   public function setDownload($download) {
-    if (!strlen($download)) {
+    if ($download === null || !strlen($download)) {
       $download = 'untitled';
     }
     $this->download = $download;
@@ -113,7 +113,8 @@
       $headers[] = array('Content-Length', $content_len);
     }
 
-    if (strlen($this->getDownload())) {
+    $download = $this->getDownload();
+    if ($download !== null && strlen($download)) {
       $headers[] = array('X-Download-Options', 'noopen');
 
       $filename = $this->getDownload();
@@ -150,7 +151,7 @@
       $begin = (int)$matches[1];
 
       // The "Range" may be "200-299" or "200-", meaning "until end of file".
-      if (strlen($matches[2])) {
+      if ($matches[2] !== null && strlen($matches[2])) {
         $range_end = (int)$matches[2];
         $end = $range_end + 1;
       } else {
diff --git a/src/aphront/response/AphrontWebpageResponse.php b/src/aphront/response/AphrontWebpageResponse.php
--- a/src/aphront/response/AphrontWebpageResponse.php
+++ b/src/aphront/response/AphrontWebpageResponse.php
@@ -21,7 +21,7 @@
 
   public function buildResponseString() {
     $unexpected_output = $this->getUnexpectedOutput();
-    if (strlen($unexpected_output)) {
+    if ($unexpected_output !== null && strlen($unexpected_output)) {
       $style = array(
         'background: linear-gradient(180deg, #eeddff, #ddbbff);',
         'white-space: pre-wrap;',
diff --git a/src/applications/auth/constants/PhabricatorCookies.php b/src/applications/auth/constants/PhabricatorCookies.php
--- a/src/applications/auth/constants/PhabricatorCookies.php
+++ b/src/applications/auth/constants/PhabricatorCookies.php
@@ -89,7 +89,7 @@
     // temporary and clearing it when users log out.
 
     $value = $request->getCookie(self::COOKIE_CLIENTID);
-    if (!strlen($value)) {
+    if ($value === null || !strlen($value)) {
       $request->setTemporaryCookie(
         self::COOKIE_CLIENTID,
         Filesystem::readRandomCharacters(16));
diff --git a/src/applications/auth/controller/PhabricatorAuthController.php b/src/applications/auth/controller/PhabricatorAuthController.php
--- a/src/applications/auth/controller/PhabricatorAuthController.php
+++ b/src/applications/auth/controller/PhabricatorAuthController.php
@@ -282,7 +282,7 @@
       $viewer,
       PhabricatorAuthLoginMessageType::MESSAGEKEY);
 
-    if (!strlen($text)) {
+    if ($text === null || !strlen($text)) {
       return null;
     }
 
diff --git a/src/applications/auth/controller/PhabricatorAuthRegisterController.php b/src/applications/auth/controller/PhabricatorAuthRegisterController.php
--- a/src/applications/auth/controller/PhabricatorAuthRegisterController.php
+++ b/src/applications/auth/controller/PhabricatorAuthRegisterController.php
@@ -18,7 +18,7 @@
     $invite = $this->loadInvite();
 
     $is_setup = false;
-    if (strlen($account_key)) {
+    if ($account_key !== null && strlen($account_key)) {
       $result = $this->loadAccountForRegistrationOrLinking($account_key);
       list($account, $provider, $response) = $result;
       $is_default = false;
@@ -244,9 +244,9 @@
 
     $require_real_name = PhabricatorEnv::getEnvConfig('user.require-real-name');
 
-    $e_username = strlen($value_username) ? null : true;
+    $e_username = phutil_nonempty_string($value_username) ? null : true;
     $e_realname = $require_real_name ? true : null;
-    $e_email = strlen($value_email) ? null : true;
+    $e_email = phutil_nonempty_string($value_email) ? null : true;
     $e_password = true;
     $e_captcha = true;
 
@@ -288,7 +288,7 @@
 
       if ($can_edit_username) {
         $value_username = $request->getStr('username');
-        if (!strlen($value_username)) {
+        if (!phutil_nonempty_string($value_username)) {
           $e_username = pht('Required');
           $errors[] = pht('Username is required.');
         } else if (!PhabricatorUser::validateUsername($value_username)) {
@@ -323,7 +323,7 @@
 
       if ($can_edit_email) {
         $value_email = $request->getStr('email');
-        if (!strlen($value_email)) {
+        if (!phutil_nonempty_string($value_email)) {
           $e_email = pht('Required');
           $errors[] = pht('Email is required.');
         } else if (!PhabricatorUserEmail::isValidAddress($value_email)) {
@@ -339,7 +339,7 @@
 
       if ($can_edit_realname) {
         $value_realname = $request->getStr('realName');
-        if (!strlen($value_realname) && $require_real_name) {
+        if (!phutil_nonempty_string($value_realname) && $require_real_name) {
           $e_realname = pht('Required');
           $errors[] = pht('Real name is required.');
         } else {
diff --git a/src/applications/auth/controller/PhabricatorAuthStartController.php b/src/applications/auth/controller/PhabricatorAuthStartController.php
--- a/src/applications/auth/controller/PhabricatorAuthStartController.php
+++ b/src/applications/auth/controller/PhabricatorAuthStartController.php
@@ -31,7 +31,7 @@
     $session_token = $request->getCookie(PhabricatorCookies::COOKIE_SESSION);
     $did_clear = $request->getStr('cleared');
 
-    if (strlen($session_token)) {
+    if ($session_token !== null && strlen($session_token)) {
       $kind = PhabricatorAuthSessionEngine::getSessionKindFromToken(
         $session_token);
       switch ($kind) {
@@ -98,7 +98,7 @@
     }
 
     $next_uri = $request->getStr('next');
-    if (!strlen($next_uri)) {
+    if (phutil_nonempty_string($next_uri)) {
       if ($this->getDelegatingController()) {
         // Only set a next URI from the request path if this controller was
         // delegated to, which happens when a user tries to view a page which
@@ -112,7 +112,7 @@
     }
 
     if (!$request->isFormPost()) {
-      if (strlen($next_uri)) {
+      if (phutil_nonempty_string($next_uri)) {
         PhabricatorCookies::setNextURICookie($request, $next_uri);
       }
       PhabricatorCookies::setClientIDCookie($request);
@@ -226,7 +226,7 @@
 
     $via_header = AphrontRequest::getViaHeaderName();
     $via_uri = AphrontRequest::getHTTPHeader($via_header);
-    if (strlen($via_uri)) {
+    if ($via_uri !== null && strlen($via_uri)) {
       PhabricatorCookies::setNextURICookie($request, $via_uri, $force = true);
     }
 
diff --git a/src/applications/auth/password/PhabricatorAuthPasswordException.php b/src/applications/auth/password/PhabricatorAuthPasswordException.php
--- a/src/applications/auth/password/PhabricatorAuthPasswordException.php
+++ b/src/applications/auth/password/PhabricatorAuthPasswordException.php
@@ -4,7 +4,7 @@
   extends Exception {
 
   private $passwordError;
-  private $confirmErorr;
+  private $confirmError;
 
   public function __construct(
     $message,
diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php
--- a/src/applications/base/controller/PhabricatorController.php
+++ b/src/applications/base/controller/PhabricatorController.php
@@ -74,7 +74,7 @@
       $session_engine = new PhabricatorAuthSessionEngine();
 
       $phsid = $request->getCookie(PhabricatorCookies::COOKIE_SESSION);
-      if (strlen($phsid)) {
+      if ($phsid !== null && strlen($phsid)) {
         $session_user = $session_engine->loadUserForSession(
           PhabricatorAuthSession::TYPE_WEB,
           $phsid);
diff --git a/src/applications/celerity/controller/CelerityResourceController.php b/src/applications/celerity/controller/CelerityResourceController.php
--- a/src/applications/celerity/controller/CelerityResourceController.php
+++ b/src/applications/celerity/controller/CelerityResourceController.php
@@ -113,7 +113,7 @@
 
     $range = AphrontRequest::getHTTPHeader('Range');
 
-    if (strlen($range)) {
+    if ($range !== null && strlen($range)) {
       $response->setContentLength(strlen($data));
 
       list($range_begin, $range_end) = $response->parseHTTPRange($range);
diff --git a/src/applications/config/check/PhabricatorBaseURISetupCheck.php b/src/applications/config/check/PhabricatorBaseURISetupCheck.php
--- a/src/applications/config/check/PhabricatorBaseURISetupCheck.php
+++ b/src/applications/config/check/PhabricatorBaseURISetupCheck.php
@@ -11,7 +11,7 @@
 
     $host_header = AphrontRequest::getHTTPHeader('Host');
     if (strpos($host_header, '.') === false) {
-      if (!strlen(trim($host_header))) {
+      if ($host_header === null || !strlen(trim($host_header))) {
         $name = pht('No "Host" Header');
         $summary = pht('No "Host" header present in request.');
         $message = pht(
diff --git a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php
--- a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php
+++ b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php
@@ -53,7 +53,7 @@
     }
 
     $expect_user = PhabricatorEnv::getEnvConfig('phd.user');
-    if (strlen($expect_user)) {
+    if ($expect_user !== null && strlen($expect_user)) {
 
       try {
         $all_daemons = id(new PhabricatorDaemonLogQuery())
diff --git a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php
--- a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php
+++ b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php
@@ -124,7 +124,7 @@
     }
 
     $open_basedir = ini_get('open_basedir');
-    if (strlen($open_basedir)) {
+    if ($open_basedir !== null && strlen($open_basedir)) {
       // If `open_basedir` is set, just fatal. It's technically possible for
       // us to run with certain values of `open_basedir`, but: we can only
       // raise fatal errors from preflight steps, so we'd have to do this check
diff --git a/src/applications/config/check/PhabricatorSetupCheck.php b/src/applications/config/check/PhabricatorSetupCheck.php
--- a/src/applications/config/check/PhabricatorSetupCheck.php
+++ b/src/applications/config/check/PhabricatorSetupCheck.php
@@ -122,7 +122,7 @@
     $db_cache = new PhabricatorKeyValueDatabaseCache();
     try {
       $value = $db_cache->getKey('phabricator.setup.issue-keys');
-      if (!strlen($value)) {
+      if ($value === null || !strlen($value)) {
         return null;
       }
       return phutil_json_decode($value);
diff --git a/src/applications/config/check/PhabricatorStorageSetupCheck.php b/src/applications/config/check/PhabricatorStorageSetupCheck.php
--- a/src/applications/config/check/PhabricatorStorageSetupCheck.php
+++ b/src/applications/config/check/PhabricatorStorageSetupCheck.php
@@ -151,19 +151,19 @@
 
     $how_many = 0;
 
-    if (strlen($access_key)) {
+    if ($access_key !== null && strlen($access_key)) {
       $how_many++;
     }
 
-    if (strlen($secret_key)) {
+    if ($secret_key !== null && strlen($secret_key)) {
       $how_many++;
     }
 
-    if (strlen($region)) {
+    if ($region !== null && strlen($region)) {
       $how_many++;
     }
 
-    if (strlen($endpoint)) {
+    if ($endpoint !== null && strlen($endpoint)) {
       $how_many++;
     }
 
diff --git a/src/applications/config/check/PhabricatorWebServerSetupCheck.php b/src/applications/config/check/PhabricatorWebServerSetupCheck.php
--- a/src/applications/config/check/PhabricatorWebServerSetupCheck.php
+++ b/src/applications/config/check/PhabricatorWebServerSetupCheck.php
@@ -23,7 +23,7 @@
     }
 
     $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
-    if (!strlen($base_uri)) {
+    if ($base_uri === null || !strlen($base_uri)) {
       // If `phabricator.base-uri` is not set then we can't really do
       // anything.
       return;
diff --git a/src/applications/config/controller/PhabricatorConfigConsoleController.php b/src/applications/config/controller/PhabricatorConfigConsoleController.php
--- a/src/applications/config/controller/PhabricatorConfigConsoleController.php
+++ b/src/applications/config/controller/PhabricatorConfigConsoleController.php
@@ -85,14 +85,14 @@
     $rows = array();
     foreach ($versions as $name => $info) {
       $branchpoint = $info['branchpoint'];
-      if (strlen($branchpoint)) {
+      if ($branchpoint !== null && strlen($branchpoint)) {
         $branchpoint = substr($branchpoint, 0, 12);
       } else {
         $branchpoint = null;
       }
 
       $version = $info['hash'];
-      if (strlen($version)) {
+      if ($version !== null && strlen($version)) {
         $version = substr($version, 0, 12);
       } else {
         $version = pht('Unknown');
diff --git a/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php b/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php
--- a/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php
+++ b/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php
@@ -837,7 +837,7 @@
 
     $parts = array();
     foreach ($properties as $key => $property) {
-      if (!strlen($property)) {
+      if ($property === null || !strlen($property)) {
         continue;
       }
 
diff --git a/src/applications/config/controller/settings/PhabricatorConfigSettingsListController.php b/src/applications/config/controller/settings/PhabricatorConfigSettingsListController.php
--- a/src/applications/config/controller/settings/PhabricatorConfigSettingsListController.php
+++ b/src/applications/config/controller/settings/PhabricatorConfigSettingsListController.php
@@ -7,7 +7,7 @@
     $viewer = $request->getViewer();
 
     $filter = $request->getURIData('filter');
-    if (!strlen($filter)) {
+    if ($filter === null || !strlen($filter)) {
       $filter = 'settings';
     }
 
diff --git a/src/applications/console/controller/DarkConsoleController.php b/src/applications/console/controller/DarkConsoleController.php
--- a/src/applications/console/controller/DarkConsoleController.php
+++ b/src/applications/console/controller/DarkConsoleController.php
@@ -26,7 +26,7 @@
     }
 
     $visible = $request->getStr('visible');
-    if (strlen($visible)) {
+    if (phutil_nonempty_string($visible)) {
       $this->writeDarkConsoleSetting(
         PhabricatorDarkConsoleVisibleSetting::SETTINGKEY,
         (int)$visible);
@@ -34,7 +34,7 @@
     }
 
     $tab = $request->getStr('tab');
-    if (strlen($tab)) {
+    if (phutil_nonempty_string($tab)) {
       $this->writeDarkConsoleSetting(
         PhabricatorDarkConsoleTabSetting::SETTINGKEY,
         $tab);
diff --git a/src/applications/console/core/DarkConsoleCore.php b/src/applications/console/core/DarkConsoleCore.php
--- a/src/applications/console/core/DarkConsoleCore.php
+++ b/src/applications/console/core/DarkConsoleCore.php
@@ -114,6 +114,9 @@
    * need to convert it to UTF-8.
    */
   private function sanitizeForJSON($data) {
+    if ($data === null) {
+      return '<null>';
+    }
     if (is_object($data)) {
       return '<object:'.get_class($data).'>';
     } else if (is_array($data)) {
diff --git a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php
--- a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php
+++ b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php
@@ -55,7 +55,7 @@
     $limit = $request->getValue('limit');
 
     if (strlen($against_hash)) {
-      $commit_range = "${against_hash}..${commit_hash}";
+      $commit_range = "{$against_hash}..{$commit_hash}";
     } else {
       $commit_range = $commit_hash;
     }
diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php
--- a/src/applications/diffusion/controller/DiffusionController.php
+++ b/src/applications/diffusion/controller/DiffusionController.php
@@ -97,19 +97,19 @@
     AphrontRequest $request) {
 
     $short_name = $request->getURIData('repositoryShortName');
-    if (strlen($short_name)) {
+    if ($short_name !== null && strlen($short_name)) {
       // If the short name ends in ".git", ignore it.
       $short_name = preg_replace('/\\.git\z/', '', $short_name);
       return $short_name;
     }
 
     $identifier = $request->getURIData('repositoryCallsign');
-    if (strlen($identifier)) {
+    if ($identifier !== null && strlen($identifier)) {
       return $identifier;
     }
 
     $id = $request->getURIData('repositoryID');
-    if (strlen($id)) {
+    if ($id !== null && strlen($id)) {
       return (int)$id;
     }
 
diff --git a/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php b/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php
--- a/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php
+++ b/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php
@@ -183,11 +183,11 @@
       'image/png');
   }
 
-  private static function rgba2gd($rgba) {
-    $r = $rgba[0];
-    $g = $rgba[1];
-    $b = $rgba[2];
-    $a = $rgba[3];
+  private static function rgba2gd(array $rgba) {
+    $r = (int)$rgba[0];
+    $g = (int)$rgba[1];
+    $b = (int)$rgba[2];
+    $a = (int)$rgba[3];
     $a = (1 - $a) * 255;
     return ($a << 24) | ($r << 16) | ($g << 8) | $b;
   }
diff --git a/src/applications/files/conduit/FileAllocateConduitAPIMethod.php b/src/applications/files/conduit/FileAllocateConduitAPIMethod.php
--- a/src/applications/files/conduit/FileAllocateConduitAPIMethod.php
+++ b/src/applications/files/conduit/FileAllocateConduitAPIMethod.php
@@ -65,7 +65,7 @@
         ->executeOne();
     }
 
-    if (strlen($name) && !$hash && !$file) {
+    if ($name !== null && strlen($name) && !$hash && !$file) {
       if ($length > PhabricatorFileStorageEngine::getChunkThreshold()) {
         // If we don't have a hash, but this file is large enough to store in
         // chunks and thus may be resumable, try to find a partially uploaded
diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php
--- a/src/applications/files/controller/PhabricatorFileDataController.php
+++ b/src/applications/files/controller/PhabricatorFileDataController.php
@@ -29,7 +29,7 @@
     $request_kind = $request->getURIData('kind');
     $is_download = ($request_kind === 'download');
 
-    if (!strlen($alt) || $main_domain == $alt_domain) {
+    if (($alt === null || !strlen($alt)) || $main_domain == $alt_domain) {
       // No alternate domain.
       $should_redirect = false;
       $is_alternate_domain = false;
@@ -69,7 +69,7 @@
     // an initial request for bytes 0-1 of the audio file, and things go south
     // if we can't respond with a 206 Partial Content.
     $range = $request->getHTTPHeader('range');
-    if (strlen($range)) {
+    if ($range !== null && strlen($range)) {
       list($begin, $end) = $response->parseHTTPRange($range);
     }
 
diff --git a/src/applications/files/controller/PhabricatorFileLightboxController.php b/src/applications/files/controller/PhabricatorFileLightboxController.php
--- a/src/applications/files/controller/PhabricatorFileLightboxController.php
+++ b/src/applications/files/controller/PhabricatorFileLightboxController.php
@@ -20,7 +20,7 @@
       return new Aphront404Response();
     }
 
-    if (strlen($comment)) {
+    if ($comment !== null && strlen($comment)) {
       $xactions = array();
       $xactions[] = id(new PhabricatorFileTransaction())
         ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
diff --git a/src/applications/files/controller/PhabricatorFileViewController.php b/src/applications/files/controller/PhabricatorFileViewController.php
--- a/src/applications/files/controller/PhabricatorFileViewController.php
+++ b/src/applications/files/controller/PhabricatorFileViewController.php
@@ -311,12 +311,12 @@
       $file->getStorageHandle());
 
     $custom_alt = $file->getCustomAltText();
-    if (strlen($custom_alt)) {
+    if ($custom_alt !== null && strlen($custom_alt)) {
       $finfo->addProperty(pht('Custom Alt Text'), $custom_alt);
     }
 
     $default_alt = $file->getDefaultAltText();
-    if (strlen($default_alt)) {
+    if ($default_alt !== null && strlen($default_alt)) {
       $finfo->addProperty(pht('Default Alt Text'), $default_alt);
     }
 
diff --git a/src/applications/files/document/PhabricatorJupyterDocumentEngine.php b/src/applications/files/document/PhabricatorJupyterDocumentEngine.php
--- a/src/applications/files/document/PhabricatorJupyterDocumentEngine.php
+++ b/src/applications/files/document/PhabricatorJupyterDocumentEngine.php
@@ -323,7 +323,7 @@
     }
 
     $nbformat = idx($data, 'nbformat');
-    if (!strlen($nbformat)) {
+    if ($nbformat == null || !strlen($nbformat)) {
       throw new Exception(
         pht(
           'This document is missing an "nbformat" field. Jupyter notebooks '.
diff --git a/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php b/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php
--- a/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php
+++ b/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php
@@ -60,12 +60,12 @@
     }
 
     $encode_setting = $request->getStr('encode');
-    if (strlen($encode_setting)) {
+    if (phutil_nonempty_string($encode_setting)) {
       $engine->setEncodingConfiguration($encode_setting);
     }
 
     $highlight_setting = $request->getStr('highlight');
-    if (strlen($highlight_setting)) {
+    if (phutil_nonempty_string($highlight_setting)) {
       $engine->setHighlightingConfiguration($highlight_setting);
     }
 
@@ -208,12 +208,12 @@
     $this->activeEngine = $engine;
 
     $encode_setting = $request->getStr('encode');
-    if (strlen($encode_setting)) {
+    if (phutil_nonempty_string($encode_setting)) {
       $engine->setEncodingConfiguration($encode_setting);
     }
 
     $highlight_setting = $request->getStr('highlight');
-    if (strlen($highlight_setting)) {
+    if (phutil_nonempty_string($highlight_setting)) {
       $engine->setHighlightingConfiguration($highlight_setting);
     }
 
diff --git a/src/applications/files/engine/PhabricatorS3FileStorageEngine.php b/src/applications/files/engine/PhabricatorS3FileStorageEngine.php
--- a/src/applications/files/engine/PhabricatorS3FileStorageEngine.php
+++ b/src/applications/files/engine/PhabricatorS3FileStorageEngine.php
@@ -31,11 +31,11 @@
     $endpoint = PhabricatorEnv::getEnvConfig('amazon-s3.endpoint');
     $region = PhabricatorEnv::getEnvConfig('amazon-s3.region');
 
-    return (strlen($bucket) &&
-      strlen($access_key) &&
-      strlen($secret_key) &&
-      strlen($endpoint) &&
-      strlen($region));
+    return ($bucket !== null && strlen($bucket) &&
+      $access_key !== null && strlen($access_key) &&
+      $secret_key !== null && strlen($secret_key) &&
+      $endpoint !== null && strlen($endpoint) &&
+      $region !== null && strlen($region));
   }
 
 
@@ -57,7 +57,7 @@
     $parts[] = 'phabricator';
 
     $instance_name = PhabricatorEnv::getEnvConfig('cluster.instance');
-    if (strlen($instance_name)) {
+    if ($instance_name !== null && strlen($instance_name)) {
       $parts[] = $instance_name;
     }
 
diff --git a/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php b/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php
--- a/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php
+++ b/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php
@@ -197,7 +197,7 @@
       $alt = $options['alt'];
     }
 
-    if (!strlen($alt)) {
+    if ($alt === null || !strlen($alt)) {
       $alt = $file->getAltText();
     }
 
@@ -346,6 +346,9 @@
   }
 
   private function parseDimension($string) {
+    if ($string === null || !strlen($string)) {
+      return null;
+    }
     $string = trim($string);
 
     if (preg_match('/^(?:\d*\\.)?\d+%?$/', $string)) {
diff --git a/src/applications/files/markup/PhabricatorImageRemarkupRule.php b/src/applications/files/markup/PhabricatorImageRemarkupRule.php
--- a/src/applications/files/markup/PhabricatorImageRemarkupRule.php
+++ b/src/applications/files/markup/PhabricatorImageRemarkupRule.php
@@ -49,7 +49,8 @@
 
     $args += $defaults;
 
-    if (!strlen($args['uri'])) {
+    $uri_arg = $args['uri'];
+    if ($uri_arg === null || !strlen($uri_arg)) {
       return $matches[0];
     }
 
@@ -57,9 +58,9 @@
     // validate it more carefully before proxying it, but if whatever the user
     // has typed isn't even close, just decline to activate the rule behavior.
     try {
-      $uri = new PhutilURI($args['uri']);
+      $uri = new PhutilURI($uri_arg);
 
-      if (!strlen($uri->getProtocol())) {
+      if ($uri->getProtocol() === null || !strlen($uri->getProtocol())) {
         return $matches[0];
       }
 
diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php
--- a/src/applications/files/storage/PhabricatorFile.php
+++ b/src/applications/files/storage/PhabricatorFile.php
@@ -288,7 +288,7 @@
     // update the parent file if a MIME type hasn't been provided. This matters
     // for large media files like video.
     $mime_type = idx($params, 'mime-type');
-    if (!strlen($mime_type)) {
+    if ($mime_type === null || !strlen($mime_type)) {
       $file->setMimeType('application/octet-stream');
     }
 
@@ -856,7 +856,7 @@
     // instance identity in the path allows us to distinguish between requests
     // originating from different instances but served through the same CDN.
     $instance = PhabricatorEnv::getEnvConfig('cluster.instance');
-    if (strlen($instance)) {
+    if ($instance !== null && strlen($instance)) {
       $parts[] = '@'.$instance;
     }
 
@@ -903,7 +903,7 @@
     $parts[] = 'xform';
 
     $instance = PhabricatorEnv::getEnvConfig('cluster.instance');
-    if (strlen($instance)) {
+    if ($instance !== null && strlen($instance)) {
       $parts[] = '@'.$instance;
     }
 
@@ -1278,7 +1278,7 @@
   public function getAltText() {
     $alt = $this->getCustomAltText();
 
-    if (strlen($alt)) {
+    if ($alt !== null && strlen($alt)) {
       return $alt;
     }
 
@@ -1309,7 +1309,7 @@
     $parts = array();
 
     $name = $this->getName();
-    if (strlen($name)) {
+    if ($name !== null && strlen($name)) {
       $parts[] = $name;
     }
 
diff --git a/src/applications/files/view/PhabricatorGlobalUploadTargetView.php b/src/applications/files/view/PhabricatorGlobalUploadTargetView.php
--- a/src/applications/files/view/PhabricatorGlobalUploadTargetView.php
+++ b/src/applications/files/view/PhabricatorGlobalUploadTargetView.php
@@ -67,7 +67,7 @@
     require_celerity_resource('global-drag-and-drop-css');
 
     $hint_text = $this->getHintText();
-    if (!strlen($hint_text)) {
+    if ($hint_text === null || !strlen($hint_text)) {
       $hint_text = "\xE2\x87\xAA ".pht('Drop Files to Upload');
     }
 
diff --git a/src/applications/herald/phid/HeraldTranscriptPHIDType.php b/src/applications/herald/phid/HeraldTranscriptPHIDType.php
--- a/src/applications/herald/phid/HeraldTranscriptPHIDType.php
+++ b/src/applications/herald/phid/HeraldTranscriptPHIDType.php
@@ -35,7 +35,7 @@
       $id = $xscript->getID();
 
       $handle->setName(pht('Transcript %s', $id));
-      $handle->setURI("/herald/transcript/${id}/");
+      $handle->setURI("/herald/transcript/{$id}/");
     }
   }
 
diff --git a/src/applications/meta/query/PhabricatorAppSearchEngine.php b/src/applications/meta/query/PhabricatorAppSearchEngine.php
--- a/src/applications/meta/query/PhabricatorAppSearchEngine.php
+++ b/src/applications/meta/query/PhabricatorAppSearchEngine.php
@@ -45,7 +45,7 @@
       ->withUnlisted(false);
 
     $name = $saved->getParameter('name');
-    if (strlen($name)) {
+    if ($name !== null && strlen($name)) {
       $query->withNameContains($name);
     }
 
diff --git a/src/applications/notification/client/PhabricatorNotificationServerRef.php b/src/applications/notification/client/PhabricatorNotificationServerRef.php
--- a/src/applications/notification/client/PhabricatorNotificationServerRef.php
+++ b/src/applications/notification/client/PhabricatorNotificationServerRef.php
@@ -144,7 +144,19 @@
   }
 
   public function getURI($to_path = null) {
-    $full_path = rtrim($this->getPath(), '/').'/'.ltrim($to_path, '/');
+    if ($to_path === null || !strlen($to_path)) {
+      $to_path = '';
+    } else {
+      $to_path = '/'.ltrim($to_path, '/');
+    }
+
+    $base_path = $this->getPath();
+    if ($base_path === null || !strlen($base_path)) {
+      $base_path = '';
+    } else {
+      $base_path = rtrim($base_path, '/');
+    }
+    $full_path = $base_path.$to_path;
 
     $uri = id(new PhutilURI('http://'.$this->getHost()))
       ->setProtocol($this->getProtocol())
@@ -152,7 +164,7 @@
       ->setPath($full_path);
 
     $instance = PhabricatorEnv::getEnvConfig('cluster.instance');
-    if (strlen($instance)) {
+    if ($instance !== null && strlen($instance)) {
       $uri->replaceQueryParam('instance', $instance);
     }
 
@@ -161,7 +173,7 @@
 
   public function getWebsocketURI($to_path = null) {
     $instance = PhabricatorEnv::getEnvConfig('cluster.instance');
-    if (strlen($instance)) {
+    if ($instance !== null && strlen($instance)) {
       $to_path = $to_path.'~'.$instance.'/';
     }
 
diff --git a/src/applications/notification/config/PhabricatorNotificationServersConfigType.php b/src/applications/notification/config/PhabricatorNotificationServersConfigType.php
--- a/src/applications/notification/config/PhabricatorNotificationServersConfigType.php
+++ b/src/applications/notification/config/PhabricatorNotificationServersConfigType.php
@@ -92,7 +92,7 @@
       }
 
       $path = idx($spec, 'path');
-      if ($type == 'admin' && strlen($path)) {
+      if ($type == 'admin' && $path !== null && strlen($path)) {
         throw $this->newException(
           pht(
             'Notification server configuration describes an invalid host '.
diff --git a/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php b/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php
--- a/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php
+++ b/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php
@@ -91,6 +91,10 @@
   }
 
   public function isRawCacheDataValid(PhabricatorUser $user, $key, $data) {
+    if ($data === null) {
+      return false;
+    }
+
     $parts = explode(',', $data, 2);
     $version = reset($parts);
     return ($version === $this->getCacheVersion($user));
diff --git a/src/applications/people/view/PhabricatorUserLogView.php b/src/applications/people/view/PhabricatorUserLogView.php
--- a/src/applications/people/view/PhabricatorUserLogView.php
+++ b/src/applications/people/view/PhabricatorUserLogView.php
@@ -36,7 +36,12 @@
 
     $rows = array();
     foreach ($logs as $log) {
-      $session = substr($log->getSession(), 0, 6);
+      // Events such as "Login Failure" will not have an associated session.
+      $session = $log->getSession();
+      if ($session === null) {
+        $session = '';
+      }
+      $session = substr($session, 0, 6);
 
       $actor_phid = $log->getActorPHID();
       $user_phid = $log->getUserPHID();
diff --git a/src/applications/search/compiler/PhutilSearchQueryCompiler.php b/src/applications/search/compiler/PhutilSearchQueryCompiler.php
--- a/src/applications/search/compiler/PhutilSearchQueryCompiler.php
+++ b/src/applications/search/compiler/PhutilSearchQueryCompiler.php
@@ -104,6 +104,9 @@
   private function tokenizeQuery($query) {
     $maximum_bytes = 1024;
 
+    if ($query === null) {
+      $query = '';
+    }
     $query_bytes = strlen($query);
     if ($query_bytes > $maximum_bytes) {
       throw new PhutilSearchQueryCompilerSyntaxException(
diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php
--- a/src/applications/search/controller/PhabricatorApplicationSearchController.php
+++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php
@@ -112,10 +112,10 @@
     $named_query = null;
     $run_query = true;
     $query_key = $this->queryKey;
-    if ($this->queryKey == 'advanced') {
+    if ($query_key == 'advanced') {
       $run_query = false;
       $query_key = $request->getStr('query');
-    } else if (!strlen($this->queryKey)) {
+    } else if ($query_key === null || !strlen($query_key)) {
       $found_query_data = false;
 
       if ($request->isHTTPGet() || $request->isQuicksand()) {
@@ -775,7 +775,7 @@
     $force_nux) {
 
     // Don't render NUX if the user has clicked away from the default page.
-    if (strlen($this->getQueryKey())) {
+    if ($this->getQueryKey() !== null && strlen($this->getQueryKey())) {
       return null;
     }
 
@@ -1022,7 +1022,7 @@
     $object_name = pht('%s %s', $object->getMonogram(), $object->getName());
 
     // Likewise, the context object can only be a dashboard.
-    if (strlen($context_phid)) {
+    if ($context_phid !== null && !strlen($context_phid)) {
       $context = id(new PhabricatorDashboardQuery())
         ->setViewer($viewer)
         ->withPHIDs(array($context_phid))
diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php
--- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php
+++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php
@@ -179,7 +179,7 @@
 
     $order = $saved->getParameter('order');
     $builtin = $query->getBuiltinOrderAliasMap();
-    if (strlen($order) && isset($builtin[$order])) {
+    if ($order !== null && strlen($order) && isset($builtin[$order])) {
       $query->setOrder($order);
     } else {
       // If the order is invalid or not available, we choose the first
@@ -872,7 +872,7 @@
   protected function readBoolFromRequest(
     AphrontRequest $request,
     $key) {
-    if (!strlen($request->getStr($key))) {
+    if (!phutil_nonempty_string($request->getStr($key))) {
       return null;
     }
     return $request->getBool($key);
@@ -895,7 +895,7 @@
    * @task dates
    */
   protected function parseDateTime($date_time) {
-    if (!strlen($date_time)) {
+    if ($date_time === null || !strlen($date_time)) {
       return null;
     }
 
@@ -916,7 +916,7 @@
 
     $start_str = $saved_query->getParameter($start_key);
     $start = null;
-    if (strlen($start_str)) {
+    if ($start_str !== null && strlen($start_str)) {
       $start = $this->parseDateTime($start_str);
       if (!$start) {
         $this->addError(
@@ -929,7 +929,7 @@
 
     $end_str = $saved_query->getParameter($end_key);
     $end = null;
-    if (strlen($end_str)) {
+    if ($end_str !== null && strlen($end_str)) {
       $end = $this->parseDateTime($end_str);
       if (!$end) {
         $this->addError(
@@ -1137,7 +1137,7 @@
     $viewer = $this->requireViewer();
 
     $query_key = $request->getValue('queryKey');
-    if (!strlen($query_key)) {
+    if ($query_key === null || !strlen($query_key)) {
       $saved_query = new PhabricatorSavedQuery();
     } else if ($this->isBuiltinQuery($query_key)) {
       $saved_query = $this->buildSavedQueryFromBuiltin($query_key);
diff --git a/src/applications/search/engine/PhabricatorProfileMenuEngine.php b/src/applications/search/engine/PhabricatorProfileMenuEngine.php
--- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php
+++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php
@@ -135,7 +135,7 @@
     if ($is_view) {
       $selected_item = $this->selectViewItem($view_list, $item_id);
     } else {
-      if (!strlen($item_id)) {
+      if ($item_id === null || !strlen($item_id)) {
         $item_id = self::ITEM_MANAGE;
       }
       $selected_item = $this->selectEditItem($view_list, $item_id);
@@ -1308,7 +1308,7 @@
     // render the default view instead.
 
     $selected_view = null;
-    if (strlen($item_id)) {
+    if ($item_id !== null && strlen($item_id)) {
       $item_views = $view_list->getViewsWithItemIdentifier($item_id);
       if ($item_views) {
         $selected_view = head($item_views);
diff --git a/src/applications/search/engine/PhabricatorProfileMenuItemView.php b/src/applications/search/engine/PhabricatorProfileMenuItemView.php
--- a/src/applications/search/engine/PhabricatorProfileMenuItemView.php
+++ b/src/applications/search/engine/PhabricatorProfileMenuItemView.php
@@ -110,7 +110,7 @@
     return $this->isExternalLink;
   }
 
-  public function setIsLabel($is_label) {
+  public function setIsLabel() {
     return $this->setSpecialType('label');
   }
 
@@ -118,7 +118,7 @@
     return $this->isSpecialType('label');
   }
 
-  public function setIsDivider($is_divider) {
+  public function setIsDivider() {
     return $this->setSpecialType('divider');
   }
 
@@ -140,7 +140,7 @@
       ->setName($this->getName());
 
     $uri = $this->getURI();
-    if (strlen($uri)) {
+    if ($uri !== null && strlen($uri)) {
       if ($this->getIsExternalLink()) {
         if (!PhabricatorEnv::isValidURIForLink($uri)) {
           $uri = '#';
@@ -176,7 +176,7 @@
     }
 
     $tooltip = $this->getTooltip();
-    if (strlen($tooltip)) {
+    if ($tooltip !== null && strlen($tooltip)) {
       $view->setTooltip($tooltip);
     }
 
diff --git a/src/applications/search/field/PhabricatorSearchDateField.php b/src/applications/search/field/PhabricatorSearchDateField.php
--- a/src/applications/search/field/PhabricatorSearchDateField.php
+++ b/src/applications/search/field/PhabricatorSearchDateField.php
@@ -17,7 +17,7 @@
   }
 
   protected function validateControlValue($value) {
-    if (!strlen($value)) {
+    if ($value === null || !strlen($value)) {
       return;
     }
 
@@ -32,7 +32,7 @@
   }
 
   protected function parseDateTime($value) {
-    if (!strlen($value)) {
+    if ($value === null || !strlen($value)) {
       return null;
     }
 
diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php
--- a/src/applications/transactions/editengine/PhabricatorEditEngine.php
+++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php
@@ -941,7 +941,7 @@
       }
     } else {
       $form_key = $request->getURIData('formKey');
-      if (strlen($form_key)) {
+      if ($form_key !== null && strlen($form_key)) {
         $config = $this->loadEditEngineConfigurationWithIdentifier($form_key);
 
         if (!$config) {
@@ -971,14 +971,14 @@
     }
 
     $page_key = $request->getURIData('pageKey');
-    if (!strlen($page_key)) {
+    if ($page_key === null || !strlen($page_key)) {
       $pages = $this->getPages($object);
       if ($pages) {
         $page_key = head_key($pages);
       }
     }
 
-    if (strlen($page_key)) {
+    if ($page_key !== null && strlen($page_key)) {
       $page = $this->selectPage($object, $page_key);
       if (!$page) {
         return new Aphront404Response();
@@ -1169,7 +1169,7 @@
       if ($this->getIsCreate()) {
         $template = $request->getStr('template');
 
-        if (strlen($template)) {
+        if (phutil_nonempty_string($template)) {
           $template_object = $this->newObjectFromIdentifier(
             $template,
             array(
@@ -1909,7 +1909,7 @@
     $comment_text = $request->getStr('comment');
 
     $comment_metadata = $request->getStr('comment_metadata');
-    if (strlen($comment_metadata)) {
+    if (phutil_nonempty_string($comment_metadata)) {
       $comment_metadata = phutil_json_decode($comment_metadata);
     }
 
@@ -2009,7 +2009,7 @@
       $xactions[] = $xaction;
     }
 
-    if (strlen($comment_text) || !$xactions) {
+    if (($comment_text !== null && strlen($comment_text)) || !$xactions) {
       $xactions[] = id(clone $template)
         ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
         ->setMetadataValue('remarkup.control', $comment_metadata)
diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php b/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php
--- a/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php
+++ b/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php
@@ -89,6 +89,9 @@
   }
 
   public function hasTagView() {
+    if ($this->getTagText() === null) {
+      return false;
+    }
     return (bool)strlen($this->getTagText());
   }
 
diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php
--- a/src/applications/transactions/editfield/PhabricatorEditField.php
+++ b/src/applications/transactions/editfield/PhabricatorEditField.php
@@ -418,7 +418,7 @@
       }
 
       $instructions = $this->getControlInstructions();
-      if (strlen($instructions)) {
+      if ($instructions !== null && strlen($instructions)) {
         $form->appendRemarkupInstructions($instructions);
       }
 
diff --git a/src/applications/transactions/editfield/PhabricatorTextEditField.php b/src/applications/transactions/editfield/PhabricatorTextEditField.php
--- a/src/applications/transactions/editfield/PhabricatorTextEditField.php
+++ b/src/applications/transactions/editfield/PhabricatorTextEditField.php
@@ -18,7 +18,7 @@
     $control = new AphrontFormTextControl();
 
     $placeholder = $this->getPlaceholder();
-    if (strlen($placeholder)) {
+    if ($placeholder !== null && strlen($placeholder)) {
       $control->setPlaceholder($placeholder);
     }
 
diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
--- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
+++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
@@ -617,7 +617,7 @@
         return true;
       case PhabricatorTransactions::TYPE_SPACE:
         $space_phid = $xaction->getNewValue();
-        if (!strlen($space_phid)) {
+        if ($space_phid === null || !strlen($space_phid)) {
           // If an install has no Spaces or the Spaces controls are not visible
           // to the viewer, we might end up with the empty string here instead
           // of a strict `null`, because some controller just used `getStr()`
diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php
--- a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php
+++ b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php
@@ -39,7 +39,7 @@
       $parameters = array();
 
       $raw_parameters = $request->getStr('parameters');
-      if (strlen($raw_parameters)) {
+      if (phutil_nonempty_string($raw_parameters)) {
         try {
           $parameters = phutil_json_decode($raw_parameters);
         } catch (PhutilJSONParserException $ex) {
diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php
--- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php
+++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php
@@ -28,7 +28,8 @@
 
     // We only need to do a prefix phase query if there's an actual query
     // string. If the user didn't type anything, nothing can possibly match it.
-    if (strlen($this->getRawQuery())) {
+    $raw_query = $this->getRawQuery();
+    if ($raw_query !== null && strlen($raw_query)) {
       $phases[] = self::PHASE_PREFIX;
     }
 
@@ -214,6 +215,9 @@
       if (!$limit) {
         $limit = count($results);
       }
+      if (!$offset) {
+        $offset = 0;
+      }
 
       $results = array_slice($results, $offset, $limit, $preserve_keys = true);
     }
diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php
--- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php
+++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php
@@ -147,6 +147,9 @@
   }
 
   public static function tokenizeString($string) {
+    if ($string === null) {
+      return array();
+    }
     $string = phutil_utf8_strtolower($string);
     $string = trim($string);
     if (!strlen($string)) {
@@ -464,7 +467,7 @@
     // We're looking for a "(" so that a string like "members(q" is identified
     // and parsed as a function call. This allows us to start generating
     // results immediately, before the user fully types out "members(quack)".
-    return (strpos($token, '(') !== false);
+    return ($token !== null && strpos($token, '(') !== false);
   }
 
 
diff --git a/src/infrastructure/cluster/PhabricatorDatabaseRef.php b/src/infrastructure/cluster/PhabricatorDatabaseRef.php
--- a/src/infrastructure/cluster/PhabricatorDatabaseRef.php
+++ b/src/infrastructure/cluster/PhabricatorDatabaseRef.php
@@ -229,7 +229,7 @@
     $host = $this->getHost();
 
     $port = $this->getPort();
-    if (strlen($port)) {
+    if ($port !== null && strlen($port)) {
       return "{$host}:{$port}";
     }
 
diff --git a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php
--- a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php
+++ b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php
@@ -89,7 +89,7 @@
       $field_handles = array_select_keys($handles, $phids[$field_key]);
 
       $instructions = $field->getInstructionsForEdit();
-      if (strlen($instructions)) {
+      if ($instructions !== null && strlen($instructions)) {
         $form->appendRemarkupInstructions($instructions);
       }
 
diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php
--- a/src/infrastructure/env/PhabricatorEnv.php
+++ b/src/infrastructure/env/PhabricatorEnv.php
@@ -125,7 +125,7 @@
     // If an instance identifier is defined, write it into the environment so
     // it's available to subprocesses.
     $instance = self::getEnvConfig('cluster.instance');
-    if (strlen($instance)) {
+    if ($instance !== null && strlen($instance)) {
       putenv('PHABRICATOR_INSTANCE='.$instance);
       $_ENV['PHABRICATOR_INSTANCE'] = $instance;
     }
diff --git a/src/infrastructure/javelin/markup.php b/src/infrastructure/javelin/markup.php
--- a/src/infrastructure/javelin/markup.php
+++ b/src/infrastructure/javelin/markup.php
@@ -77,7 +77,10 @@
   $is_post = (strcasecmp($http_method, 'POST') === 0);
 
   $http_action = idx($attributes, 'action');
-  $is_absolute_uri = preg_match('#^(https?:|//)#', $http_action);
+  $is_absolute_uri = false;
+  if ($http_action != null) {
+    $is_absolute_uri = preg_match('#^(https?:|//)#', $http_action);
+  }
 
   if ($is_post) {
 
diff --git a/src/view/control/AphrontTableView.php b/src/view/control/AphrontTableView.php
--- a/src/view/control/AphrontTableView.php
+++ b/src/view/control/AphrontTableView.php
@@ -135,7 +135,7 @@
 
     $col_classes = array();
     foreach ($this->columnClasses as $key => $class) {
-      if (strlen($class)) {
+      if ($class !== null && strlen($class)) {
         $col_classes[] = $class;
       } else {
         $col_classes[] = null;
diff --git a/src/view/form/control/AphrontFormControl.php b/src/view/form/control/AphrontFormControl.php
--- a/src/view/form/control/AphrontFormControl.php
+++ b/src/view/form/control/AphrontFormControl.php
@@ -109,7 +109,7 @@
   }
 
   public function isEmpty() {
-    return !strlen($this->getValue());
+    return $this->getValue() === null || !strlen($this->getValue());
   }
 
   public function getSerializedValue() {
@@ -171,9 +171,8 @@
       array('class' => 'aphront-form-input'),
       $this->renderInput());
 
-    $error = null;
-    if (strlen($this->getError())) {
-      $error = $this->getError();
+    $error = $this->error;
+    if ($error !== null && strlen($error)) {
       if ($error === true) {
         $error = phutil_tag(
           'span',
@@ -185,9 +184,12 @@
           array('class' => 'aphront-form-error'),
           $error);
       }
+    } else {
+      $error = null;
     }
 
-    if (strlen($this->getLabel())) {
+    $label = $this->label;
+    if ($label !== null && strlen($label)) {
       $label = phutil_tag(
         'label',
         array(
@@ -203,7 +205,8 @@
       $custom_class .= ' aphront-form-control-nolabel';
     }
 
-    if (strlen($this->getCaption())) {
+    $caption = $this->caption;
+    if ($caption !== null && strlen($caption)) {
       $caption = phutil_tag(
         'div',
         array('class' => 'aphront-form-caption'),
diff --git a/src/view/form/control/AphrontFormTokenizerControl.php b/src/view/form/control/AphrontFormTokenizerControl.php
--- a/src/view/form/control/AphrontFormTokenizerControl.php
+++ b/src/view/form/control/AphrontFormTokenizerControl.php
@@ -67,8 +67,8 @@
     }
     $datasource->setViewer($this->getUser());
 
-    $placeholder = null;
-    if (!strlen($this->placeholder)) {
+    $placeholder = $this->placeholder;
+    if ($placeholder === null || !strlen($placeholder)) {
       $placeholder = $datasource->getPlaceholderText();
     }
 
diff --git a/src/view/layout/AphrontSideNavFilterView.php b/src/view/layout/AphrontSideNavFilterView.php
--- a/src/view/layout/AphrontSideNavFilterView.php
+++ b/src/view/layout/AphrontSideNavFilterView.php
@@ -97,11 +97,10 @@
       ->setName($name)
       ->setType($type);
 
-    if (strlen($icon)) {
+    if ($icon !== null) {
       $item->setIcon($icon);
     }
 
-
     if (strlen($key)) {
       $item->setKey($key);
     }
@@ -145,7 +144,7 @@
 
   public function selectFilter($key, $default = null) {
     $this->selectedFilter = $default;
-    if ($this->menu->getItem($key) && strlen($key)) {
+    if ($key !== null && strlen($key) && $this->menu->getItem($key)) {
       $this->selectedFilter = $key;
     }
     return $this->selectedFilter;
diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php
--- a/src/view/page/PhabricatorStandardPageView.php
+++ b/src/view/page/PhabricatorStandardPageView.php
@@ -188,7 +188,7 @@
       }
     }
 
-    if (strlen($prefix)) {
+    if ($prefix !== null && strlen($prefix)) {
       $title = $prefix.' '.$title;
     }
 
diff --git a/src/view/page/menu/PhabricatorMainMenuView.php b/src/view/page/menu/PhabricatorMainMenuView.php
--- a/src/view/page/menu/PhabricatorMainMenuView.php
+++ b/src/view/page/menu/PhabricatorMainMenuView.php
@@ -333,7 +333,7 @@
 
 
     $wordmark_text = PhabricatorCustomLogoConfigType::getLogoWordmark();
-    if (!strlen($wordmark_text)) {
+    if ($wordmark_text === null || !strlen($wordmark_text)) {
       $wordmark_text = PlatformSymbols::getPlatformServerName();
     }
 
diff --git a/src/view/phui/PHUIInfoView.php b/src/view/phui/PHUIInfoView.php
--- a/src/view/phui/PHUIInfoView.php
+++ b/src/view/phui/PHUIInfoView.php
@@ -44,8 +44,8 @@
     return $this;
   }
 
-  public function setIsHidden($bool) {
-    $this->isHidden = $bool;
+  public function setIsHidden($is_hidden) {
+    $this->isHidden = $is_hidden;
     return $this;
   }
 
@@ -147,7 +147,7 @@
     }
 
     $title = $this->title;
-    if ($title || strlen($title)) {
+    if ($title !== null && strlen($title)) {
       $title = phutil_tag(
         'h1',
         array(
diff --git a/src/view/phui/PHUIObjectItemListView.php b/src/view/phui/PHUIObjectItemListView.php
--- a/src/view/phui/PHUIObjectItemListView.php
+++ b/src/view/phui/PHUIObjectItemListView.php
@@ -120,7 +120,7 @@
     require_celerity_resource('phui-oi-color-css');
 
     $header = null;
-    if (strlen($this->header)) {
+    if ($this->header !== null && strlen($this->header)) {
       $header = phutil_tag(
         'h1',
         array(
@@ -141,7 +141,7 @@
       }
 
       $items = $this->items;
-    } else if ($this->allowEmptyList) {
+    } else if ($this->getAllowEmptyList()) {
       $items = null;
     } else {
       $string = nonempty($this->noDataString, pht('No data.'));
diff --git a/src/view/phui/PHUIObjectItemView.php b/src/view/phui/PHUIObjectItemView.php
--- a/src/view/phui/PHUIObjectItemView.php
+++ b/src/view/phui/PHUIObjectItemView.php
@@ -97,6 +97,10 @@
     return $this;
   }
 
+  public function getHeader() {
+    return $this->header;
+  }
+
   public function setSubHead($subhead) {
     $this->subhead = $subhead;
     return $this;
@@ -122,10 +126,6 @@
     return $this->titleText;
   }
 
-  public function getHeader() {
-    return $this->header;
-  }
-
   public function addByline($byline) {
     $this->bylines[] = $byline;
     return $this;
@@ -659,8 +659,12 @@
         $this->getImageIcon());
     }
 
-    if ($image && (strlen($this->href) || strlen($this->imageHref))) {
-      $image_href = ($this->imageHref) ? $this->imageHref : $this->href;
+    $image_href = $this->href;
+    if ($image_href === null || !strlen($image_href)) {
+      $image_href = $this->imageHref;
+    }
+
+    if ($image && $image_href !== null && strlen($image_href)) {
       $image = phutil_tag(
         'a',
         array(
@@ -873,7 +877,7 @@
       'class' => 'phui-oi-status-icon',
     );
 
-    if (strlen($label)) {
+    if ($label !== null && strlen($label)) {
       $options['sigil'] = 'has-tooltip';
       $options['meta']  = array('tip' => $label, 'size' => 300);
     }
diff --git a/src/view/phui/PHUITagView.php b/src/view/phui/PHUITagView.php
--- a/src/view/phui/PHUITagView.php
+++ b/src/view/phui/PHUITagView.php
@@ -105,6 +105,10 @@
     return $this;
   }
 
+  public function getHref() {
+    return $this->href;
+  }
+
   public function setClosed($closed) {
     $this->closed = $closed;
     return $this;
@@ -126,7 +130,7 @@
   }
 
   protected function getTagName() {
-    return strlen($this->href) ? 'a' : 'span';
+    return ($this->href !== null && strlen($this->href)) ? 'a' : 'span';
   }
 
   public function setContextObject($context_object) {
diff --git a/src/view/widget/AphrontStackTraceView.php b/src/view/widget/AphrontStackTraceView.php
--- a/src/view/widget/AphrontStackTraceView.php
+++ b/src/view/widget/AphrontStackTraceView.php
@@ -30,7 +30,7 @@
       $relative = $file;
       foreach ($libraries as $library) {
         $root = phutil_get_library_root($library);
-        if (Filesystem::isDescendant($file, $root)) {
+        if ($file !== null && Filesystem::isDescendant($file, $root)) {
           $lib = $library;
           $relative = Filesystem::readablePath($file, $root);
           break;