diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php
index 381b1143993301cf34dba9e1fb85f21c57d9ec49..0a3e1f78bf45ecf10c5e700da2b73a037e25ac2c 100755
--- a/src/php/lib/Grpc/BaseStub.php
+++ b/src/php/lib/Grpc/BaseStub.php
@@ -114,7 +114,7 @@ class BaseStub {
       return true;
     }
     if ($new_state == \Grpc\CHANNEL_FATAL_FAILURE) {
-      throw new Exception('Failed to connect to server');
+      throw new \Exception('Failed to connect to server');
     }
     return false;
   }
@@ -153,6 +153,25 @@ class BaseStub {
     return array($metadata_copy, $timeout);
   }
 
+  /**
+   * validate and normalize the metadata array
+   * @param $metadata The metadata map
+   * @return $metadata Validated and key-normalized metadata map
+   * @throw InvalidArgumentException if key contains invalid characters
+   */
+  private function _validate_and_normalize_metadata($metadata) {
+    $metadata_copy = array();
+    foreach ($metadata as $key => $value) {
+      if (!preg_match('/^[A-Za-z\d_-]+$/', $key)) {
+        throw new \InvalidArgumentException(
+            'Metadata keys must be nonempty strings containing only '.
+            'alphanumeric characters, hyphens and underscores');
+      }
+      $metadata_copy[strtolower($key)] = $value;
+    }
+    return $metadata_copy;
+  }
+
   /* This class is intended to be subclassed by generated code, so all functions
      begin with "_" to avoid name collisions. */
 
@@ -178,6 +197,7 @@ class BaseStub {
                                         $actual_metadata,
                                         $jwt_aud_uri);
     }
+    $actual_metadata = $this->_validate_and_normalize_metadata($actual_metadata);
     $call->start($argument, $actual_metadata, $options);
     return $call;
   }
@@ -204,6 +224,7 @@ class BaseStub {
                                         $actual_metadata,
                                         $jwt_aud_uri);
     }
+    $actual_metadata = $this->_validate_and_normalize_metadata($actual_metadata);
     $call->start($actual_metadata);
     return $call;
   }
@@ -231,6 +252,7 @@ class BaseStub {
                                         $actual_metadata,
                                         $jwt_aud_uri);
     }
+    $actual_metadata = $this->_validate_and_normalize_metadata($actual_metadata);
     $call->start($argument, $actual_metadata, $options);
     return $call;
   }
@@ -254,6 +276,7 @@ class BaseStub {
                                         $actual_metadata,
                                         $jwt_aud_uri);
     }
+    $actual_metadata = $this->_validate_and_normalize_metadata($actual_metadata);
     $call->start($actual_metadata);
     return $call;
   }
diff --git a/src/php/tests/generated_code/AbstractGeneratedCodeTest.php b/src/php/tests/generated_code/AbstractGeneratedCodeTest.php
index 9cee1886666c0011b03ef703c8de9615889eb92e..5cdba1e5a0fde706f4bb9501cab8d496f6895d55 100644
--- a/src/php/tests/generated_code/AbstractGeneratedCodeTest.php
+++ b/src/php/tests/generated_code/AbstractGeneratedCodeTest.php
@@ -51,6 +51,14 @@ abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase {
     $this->assertTrue(is_string(self::$client->getTarget()));
   }
 
+  /**
+   * @expectedException InvalidArgumentException
+   */
+  public function testInvalidMetadata() {
+    $div_arg = new math\DivArgs();
+    $call = self::$client->Div($div_arg, array(' ' => 'abc123'));
+  }
+
   public function testWriteFlags() {
     $div_arg = new math\DivArgs();
     $div_arg->setDividend(7);