Add mongodb pluhgin. (#83)

* Add mongodb pluhgin.

* Update documents.

* Add integration tests.

* Fix CI.
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 3136a71..5fd122f 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -106,7 +106,7 @@
             bcmath, calendar, ctype, dom, exif, gettext, iconv, intl, json, mbstring,
             mysqli, mysqlnd, opcache, pdo, pdo_mysql, phar, posix, readline, redis,
             memcached, swoole-${{ matrix.version.swoole }}, xml, xmlreader, xmlwriter,
-            yaml, zip
+            yaml, zip, mongodb
 
       - name: Setup php-fpm for Linux
         if: matrix.os == 'ubuntu-20.04'
diff --git a/README.md b/README.md
index a3b339a..651bb8d 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@
   * [ ] [php-rdkafka](https://github.com/arnaud-lb/php-rdkafka)
   * [x] [predis](https://github.com/predis/predis)
   * [x] [php-amqplib](https://github.com/php-amqplib/php-amqplib) for Message Queuing Producer
+  * [x] [MongoDB](https://www.php.net/manual/en/set.mongodb.php)
 
 * Swoole Ecosystem
 
diff --git a/Vagrantfile b/Vagrantfile
index e826de0..b58e0aa 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -24,6 +24,7 @@
     config.vm.network "forwarded_port", guest: 6379, host: 6379
     config.vm.network "forwarded_port", guest: 11211, host: 11211
     config.vm.network "forwarded_port", guest: 5672, host: 5672
+    config.vm.network "forwarded_port", guest: 27017, host: 27017
 
     config.vm.synced_folder ".", "/vagrant"
 
diff --git a/docker-compose.yml b/docker-compose.yml
index 825d64a..3cc802f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -51,3 +51,11 @@
     environment:
       - RABBITMQ_DEFAULT_USER=guest
       - RABBITMQ_DEFAULT_PASS=guest
+
+  mongo:
+    image: mongo:4.4.10
+    ports:
+      - "27017:27017"
+    environment:
+      MONGO_INITDB_ROOT_USERNAME: root
+      MONGO_INITDB_ROOT_PASSWORD: example
diff --git a/docs/en/setup/service-agent/php-agent/Supported-list.md b/docs/en/setup/service-agent/php-agent/Supported-list.md
index 1f99cce..9ad8476 100644
--- a/docs/en/setup/service-agent/php-agent/Supported-list.md
+++ b/docs/en/setup/service-agent/php-agent/Supported-list.md
@@ -14,8 +14,9 @@
 * [MySQL Improved](https://www.php.net/manual/en/book.mysqli.php)
 * [Memcached](https://www.php.net/manual/en/book.memcached.php)
 * [phpredis](https://github.com/phpredis/phpredis)
-* [php-amqplib](https://github.com/php-amqplib/php-amqplib) for Message Queuing Producer
+* [MongoDB](https://www.php.net/manual/en/set.mongodb.php)
 
 ## Supported PHP library
 
 * [predis](https://github.com/predis/predis)
+* [php-amqplib](https://github.com/php-amqplib/php-amqplib) for Message Queuing Producer
diff --git a/src/component.rs b/src/component.rs
index 6e42e71..59adfff 100644
--- a/src/component.rs
+++ b/src/component.rs
@@ -25,3 +25,4 @@
 pub const COMPONENT_PHP_MEMCACHED_ID: i32 = 20;
 pub const COMPONENT_PHP_REDIS_ID: i32 = 7;
 pub const COMPONENT_AMQP_PRODUCER_ID: i32 = 144;
+pub const COMPONENT_MONGODB_ID: i32 = 9;
diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs
index 71acd06..8ee7c16 100644
--- a/src/plugin/mod.rs
+++ b/src/plugin/mod.rs
@@ -16,6 +16,7 @@
 mod plugin_amqplib;
 mod plugin_curl;
 mod plugin_memcached;
+mod plugin_mongodb;
 mod plugin_mysqli;
 mod plugin_pdo;
 mod plugin_predis;
@@ -41,6 +42,7 @@
         Box::<plugin_memcached::MemcachedPlugin>::default(),
         Box::<plugin_redis::RedisPlugin>::default(),
         Box::<plugin_amqplib::AmqplibPlugin>::default(),
+        Box::<plugin_mongodb::MongodbPlugin>::default(),
     ]
 });
 
diff --git a/src/plugin/plugin_mongodb.rs b/src/plugin/plugin_mongodb.rs
new file mode 100644
index 0000000..53b58ab
--- /dev/null
+++ b/src/plugin/plugin_mongodb.rs
@@ -0,0 +1,191 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use super::{log_exception, Plugin};
+use crate::{
+    component::COMPONENT_MONGODB_ID,
+    context::RequestContext,
+    execute::{get_this_mut, AfterExecuteHook, BeforeExecuteHook},
+    tag::TAG_DB_TYPE,
+};
+use phper::{
+    objects::ZObj,
+    values::{ExecuteData, ZVal},
+};
+use skywalking::{
+    proto::v3::SpanLayer,
+    trace::span::{AbstractSpan, Span},
+};
+use std::any::Any;
+use tracing::{debug, error};
+
+const MANAGER_CLASS_NAME: &str = r"MongoDB\Driver\Manager";
+
+#[derive(Default, Clone)]
+pub struct MongodbPlugin;
+
+impl Plugin for MongodbPlugin {
+    #[inline]
+    fn class_names(&self) -> Option<&'static [&'static str]> {
+        Some(&[MANAGER_CLASS_NAME])
+    }
+
+    #[inline]
+    fn function_name_prefix(&self) -> Option<&'static str> {
+        None
+    }
+
+    fn hook(
+        &self, class_name: Option<&str>, function_name: &str,
+    ) -> Option<(Box<BeforeExecuteHook>, Box<AfterExecuteHook>)> {
+        match (class_name, function_name) {
+            (Some(MANAGER_CLASS_NAME), f)
+                if ["executebulkwrite", "executequery"].contains(&&*f.to_ascii_lowercase()) =>
+            {
+                Some(self.hook_manager_execute_namespace_method(function_name))
+            }
+            (Some(MANAGER_CLASS_NAME), f)
+                if [
+                    "executecommand",
+                    "executereadcommand",
+                    "executereadwritecommand",
+                    "executewritecommand",
+                ]
+                .contains(&&*f.to_ascii_lowercase()) =>
+            {
+                Some(self.hook_manager_execute_db_method(function_name))
+            }
+            _ => None,
+        }
+    }
+}
+
+impl MongodbPlugin {
+    fn hook_manager_execute_namespace_method(
+        &self, function_name: &str,
+    ) -> (Box<BeforeExecuteHook>, Box<AfterExecuteHook>) {
+        let function_name = function_name.to_owned();
+        (
+            Box::new(move |request_id, execute_data| {
+                before_manager_crud_hook(
+                    request_id,
+                    execute_data,
+                    &function_name,
+                    CrudScope::Namespace,
+                )
+            }),
+            Box::new(after_manager_crud_hook),
+        )
+    }
+
+    fn hook_manager_execute_db_method(
+        &self, function_name: &str,
+    ) -> (Box<BeforeExecuteHook>, Box<AfterExecuteHook>) {
+        let function_name = function_name.to_owned();
+        (
+            Box::new(move |request_id, execute_data| {
+                before_manager_crud_hook(request_id, execute_data, &function_name, CrudScope::Db)
+            }),
+            Box::new(after_manager_crud_hook),
+        )
+    }
+}
+
+enum CrudScope {
+    Namespace,
+    Db,
+}
+
+fn before_manager_crud_hook(
+    request_id: Option<i64>, execute_data: &mut ExecuteData, function_name: &str, scope: CrudScope,
+) -> crate::Result<Box<dyn Any>> {
+    let this = get_this_mut(execute_data)?;
+    let handle = this.handle();
+    debug!(handle, function_name, "call MongoDB Manager CRUD method");
+
+    let mut span = RequestContext::try_with_global_ctx(request_id, |ctx| {
+        // Since the driver connects to the database lazily, peer here is empty and
+        // reset it in after hook.
+        Ok(ctx.create_exit_span(&format!("{}->{}", MANAGER_CLASS_NAME, function_name), ""))
+    })?;
+
+    let mut span_object = span.span_object_mut();
+    span_object.set_span_layer(SpanLayer::Database);
+    span_object.component_id = COMPONENT_MONGODB_ID;
+    span_object.add_tag(TAG_DB_TYPE, "MongoDB");
+
+    if let Some(id) = execute_data
+        .get_parameter(0)
+        .as_z_str()
+        .and_then(|s| s.to_str().ok())
+    {
+        match scope {
+            CrudScope::Namespace => {
+                let mut segments = id.split('.');
+                if let Some(db) = segments.next() {
+                    span_object.add_tag("mongo.db", db);
+                }
+                if let Some(collection) = segments.next() {
+                    span_object.add_tag("mongo.collection", collection);
+                }
+            }
+            CrudScope::Db => {
+                span_object.add_tag("mongo.db", id);
+            }
+        }
+    }
+
+    Ok(Box::new(span))
+}
+
+fn after_manager_crud_hook(
+    _: Option<i64>, span: Box<dyn Any>, execute_data: &mut ExecuteData, _return_value: &mut ZVal,
+) -> crate::Result<()> {
+    let mut span = span.downcast::<Span>().unwrap();
+
+    let this = get_this_mut(execute_data)?;
+    let peer = match get_peer(this) {
+        Ok(peer) => peer,
+        Err(err) => {
+            error!(?err, "get peer failed");
+            "".to_string()
+        }
+    };
+    span.span_object_mut().peer = peer;
+
+    log_exception(&mut *span);
+
+    Ok(())
+}
+
+fn get_peer(this: &mut ZObj) -> phper::Result<String> {
+    let mut addr = Vec::new();
+
+    let mut servers = this.call("getServers", [])?;
+    let servers = servers.expect_mut_z_arr()?;
+
+    for (_, server) in servers.iter_mut() {
+        let server = server.expect_mut_z_obj()?;
+
+        let host = server.call("getHost", [])?;
+        let host = host.expect_z_str()?.to_str()?;
+
+        let port = server.call("getPort", [])?.expect_long()?;
+
+        addr.push(format!("{}:{}", host, port));
+    }
+
+    Ok(addr.join(";"))
+}
diff --git a/tests/data/expected_context.yaml b/tests/data/expected_context.yaml
index 7ff9a3d..3b45f41 100644
--- a/tests/data/expected_context.yaml
+++ b/tests/data/expected_context.yaml
@@ -15,7 +15,7 @@
 
 segmentItems:
   - serviceName: skywalking-agent-test-1
-    segmentSize: 17
+    segmentSize: 18
     segments:
       - segmentId: "not null"
         spans:
@@ -574,13 +574,13 @@
             tags:
               - { key: db.type, value: mysql }
               - {
-                key: db.data_source,
-                value: "dbname=skywalking;host=127.0.0.1:3306;",
-              }
+                  key: db.data_source,
+                  value: "dbname=skywalking;host=127.0.0.1:3306;",
+                }
               - {
-                key: db.statement,
-                value: "SELECT * FROM `mysql`.`user` WHERE `User` = :user",
-              }
+                  key: db.statement,
+                  value: "SELECT * FROM `mysql`.`user` WHERE `User` = :user",
+                }
           - operationName: PDOStatement->execute
             parentSpanId: 0
             spanId: 9
@@ -595,13 +595,13 @@
             tags:
               - { key: db.type, value: mysql }
               - {
-                key: db.data_source,
-                value: "dbname=skywalking;host=127.0.0.1:3306;",
-              }
+                  key: db.data_source,
+                  value: "dbname=skywalking;host=127.0.0.1:3306;",
+                }
               - {
-                key: db.statement,
-                value: "SELECT * FROM `mysql`.`user` WHERE `User` = :user",
-              }
+                  key: db.statement,
+                  value: "SELECT * FROM `mysql`.`user` WHERE `User` = :user",
+                }
           - operationName: PDOStatement->fetchAll
             parentSpanId: 0
             spanId: 10
@@ -616,13 +616,13 @@
             tags:
               - { key: db.type, value: mysql }
               - {
-                key: db.data_source,
-                value: "dbname=skywalking;host=127.0.0.1:3306;",
-              }
+                  key: db.data_source,
+                  value: "dbname=skywalking;host=127.0.0.1:3306;",
+                }
               - {
-                key: db.statement,
-                value: "SELECT * FROM `mysql`.`user` WHERE `User` = :user",
-              }
+                  key: db.statement,
+                  value: "SELECT * FROM `mysql`.`user` WHERE `User` = :user",
+                }
           - operationName: PDO->__construct
             parentSpanId: 0
             spanId: 11
@@ -654,13 +654,10 @@
             tags:
               - { key: db.type, value: mysql }
               - {
-                key: db.data_source,
-                value: "dbname=skywalking;host=127.0.0.1;port=3306",
-              }
-              - {
-                key: db.statement,
-                value: "SELECT * FROM not_exist",
-              }
+                  key: db.data_source,
+                  value: "dbname=skywalking;host=127.0.0.1;port=3306",
+                }
+              - { key: db.statement, value: "SELECT * FROM not_exist" }
           - operationName: PDOStatement->execute
             parentSpanId: 0
             spanId: 13
@@ -675,13 +672,10 @@
             tags:
               - { key: db.type, value: mysql }
               - {
-                key: db.data_source,
-                value: "dbname=skywalking;host=127.0.0.1;port=3306",
-              }
-              - {
-                key: db.statement,
-                value: "SELECT * FROM not_exist",
-              }
+                  key: db.data_source,
+                  value: "dbname=skywalking;host=127.0.0.1;port=3306",
+                }
+              - { key: db.statement, value: "SELECT * FROM not_exist" }
             logs:
               - logEvent:
                   - { key: error.kind, value: PDOException }
@@ -781,7 +775,7 @@
               - { key: url, value: "http://127.0.0.1:9011/predis.php" }
               - { key: http.method, value: GET }
               - { key: http.status_code, value: "200" }
-      - segmentId: 'not null'
+      - segmentId: "not null"
         spans:
           - operationName: mysqli->__construct
             parentSpanId: 0
@@ -808,11 +802,8 @@
             peer: 127.0.0.1:3306
             skipAnalysis: false
             tags:
-              - {key: db.type, value: mysql}
-              - {
-                  key: db.statement,
-                  value: "SELECT 1",
-                }
+              - { key: db.type, value: mysql }
+              - { key: db.statement, value: "SELECT 1" }
           - operationName: mysqli->__construct
             parentSpanId: 0
             spanId: 3
@@ -838,7 +829,7 @@
             peer: 127.0.0.1:3306
             skipAnalysis: false
             tags:
-              - {key: db.type, value: mysql}
+              - { key: db.type, value: mysql }
               - {
                   key: db.statement,
                   value: "SELECT * FROM `mysql`.`user` WHERE `User` = 'root'",
@@ -858,7 +849,7 @@
               - { key: url, value: "http://127.0.0.1:9011/mysqli.php" }
               - { key: http.method, value: GET }
               - { key: http.status_code, value: "200" }
-      - segmentId: 'not null'
+      - segmentId: "not null"
         spans:
           - operationName: Memcached->set
             parentSpanId: 0
@@ -1179,6 +1170,89 @@
               - { key: url, value: "http://127.0.0.1:9011/rabbitmq.php" }
               - { key: http.method, value: GET }
               - { key: http.status_code, value: "200" }
+      - segmentId: "not null"
+        spans:
+          - operationName: "MongoDB\\Driver\\Manager->executeCommand"
+            parentSpanId: 0
+            spanId: 1
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 9
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:27017
+            skipAnalysis: false
+            tags:
+              - { key: db.type, value: MongoDB }
+              - { key: mongo.db, value: admin }
+          - operationName: "MongoDB\\Driver\\Manager->executeBulkWrite"
+            parentSpanId: 0
+            spanId: 2
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 9
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:27017
+            skipAnalysis: false
+            tags:
+              - { key: db.type, value: MongoDB }
+              - { key: mongo.db, value: my_db }
+              - { key: mongo.collection, value: my_collection }
+          - operationName: "MongoDB\\Driver\\Manager->executeQuery"
+            parentSpanId: 0
+            spanId: 3
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 9
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:27017
+            skipAnalysis: false
+            tags:
+              - { key: db.type, value: MongoDB }
+              - { key: mongo.db, value: my_db }
+              - { key: mongo.collection, value: my_collection }
+          - operationName: "MongoDB\\Driver\\Manager->executeCommand"
+            parentSpanId: 0
+            spanId: 4
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 9
+            isError: true
+            spanType: Exit
+            peer: ""
+            skipAnalysis: false
+            tags:
+              - { key: db.type, value: MongoDB }
+              - { key: mongo.db, value: admin }
+            logs:
+              - logEvent:
+                  - {
+                      key: error.kind,
+                      value: "MongoDB\\Driver\\Exception\\ConnectionTimeoutException",
+                    }
+                  - { key: message, value: "not null" }
+                  - { key: stack, value: "not null" }
+          - operationName: GET:/mongodb.php
+            parentSpanId: -1
+            spanId: 0
+            spanLayer: Http
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 8001
+            isError: false
+            spanType: Entry
+            peer: ""
+            skipAnalysis: false
+            tags:
+              - { key: url, value: "http://127.0.0.1:9011/mongodb.php" }
+              - { key: http.method, value: GET }
+              - { key: http.status_code, value: "200" }
   - serviceName: skywalking-agent-test-2
     segmentSize: 1
     segments:
@@ -1301,7 +1375,7 @@
               - { key: http.method, value: GET }
               - { key: http.status_code, value: "200" }
   - serviceName: skywalking-agent-test-2-swoole
-    segmentSize: 8
+    segmentSize: 9
     segments:
       - segmentId: "not null"
         spans:
@@ -1674,3 +1748,34 @@
               - { key: url, value: "http://127.0.0.1:9502/predis" }
               - { key: http.method, value: GET }
               - { key: http.status_code, value: "200" }
+      - segmentId: "not null"
+        spans:
+          - operationName: "MongoDB\\Driver\\Manager->executeCommand"
+            parentSpanId: 0
+            spanId: 1
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 9
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:27017
+            skipAnalysis: false
+            tags:
+              - { key: db.type, value: MongoDB }
+              - { key: mongo.db, value: admin }
+          - operationName: GET:/mongodb
+            parentSpanId: -1
+            spanId: 0
+            spanLayer: Http
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 8001
+            isError: false
+            spanType: Entry
+            peer: ""
+            skipAnalysis: false
+            tags:
+              - { key: url, value: "http://127.0.0.1:9502/mongodb" }
+              - { key: http.method, value: GET }
+              - { key: http.status_code, value: "200" }
diff --git a/tests/e2e.rs b/tests/e2e.rs
index 00b1f72..90ad6e3 100644
--- a/tests/e2e.rs
+++ b/tests/e2e.rs
@@ -56,6 +56,7 @@
     request_fpm_memcached().await;
     request_fpm_redis().await;
     request_fpm_rabbitmq().await;
+    request_fpm_mongodb().await;
     request_swoole_curl().await;
     request_swoole_2_curl().await;
     request_swoole_2_pdo().await;
@@ -63,6 +64,7 @@
     request_swoole_2_memcached().await;
     request_swoole_2_redis().await;
     request_swoole_2_predis().await;
+    request_swoole_2_mongodb().await;
     sleep(Duration::from_secs(3)).await;
     request_collector_validate().await;
 }
@@ -140,6 +142,14 @@
     .await;
 }
 
+async fn request_fpm_mongodb() {
+    request_common(
+        HTTP_CLIENT.get(format!("http://{}/mongodb.php", PROXY_SERVER_1_ADDRESS)),
+        "ok",
+    )
+    .await;
+}
+
 async fn request_swoole_curl() {
     request_common(
         HTTP_CLIENT.get(format!("http://{}/curl", SWOOLE_SERVER_1_ADDRESS)),
@@ -196,6 +206,14 @@
     .await;
 }
 
+async fn request_swoole_2_mongodb() {
+    request_common(
+        HTTP_CLIENT.get(format!("http://{}/mongodb", SWOOLE_SERVER_2_ADDRESS)),
+        "ok",
+    )
+    .await;
+}
+
 async fn request_collector_validate() {
     request_common(
         HTTP_CLIENT
diff --git a/tests/php/fpm/mongodb.php b/tests/php/fpm/mongodb.php
new file mode 100644
index 0000000..c4d20e5
--- /dev/null
+++ b/tests/php/fpm/mongodb.php
@@ -0,0 +1,45 @@
+<?php
+
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+$manager = new MongoDB\Driver\Manager("mongodb://root:example@127.0.0.1:27017");
+
+{
+    $command = new MongoDB\Driver\Command(['ping' => 1]);
+    $manager->executeCommand('admin', $command);
+}
+
+{
+    $bulk = new MongoDB\Driver\BulkWrite;
+    $bulk->insert(['x' => 1, 'y' => 'foo']);
+    $bulk->insert(['x' => 2, 'y' => 'bar']);
+    $bulk->insert(['x' => 3, 'y' => 'baz']);
+    $manager->executeBulkWrite('my_db.my_collection', $bulk);
+}
+
+{
+    $query = new MongoDB\Driver\Query(['x' => 1], []);
+    $manager->executeQuery('my_db.my_collection', $query);
+}
+
+try {
+    $manager2 = new MongoDB\Driver\Manager("mongodb://root:example@127.0.0.1:27018,127.0.0.1:27019");
+    $command = new MongoDB\Driver\Command(['ping' => 1]);
+    $manager2->executeCommand('admin', $command);
+} catch(MongoDB\Driver\Exception\ConnectionTimeoutException $e) {
+}
+
+echo "ok";
diff --git a/tests/php/swoole/main.2.php b/tests/php/swoole/main.2.php
index 47cfcc0..9d8f0d8 100644
--- a/tests/php/swoole/main.2.php
+++ b/tests/php/swoole/main.2.php
@@ -97,6 +97,14 @@
                 Assert::same($client->get('foo002'), 'bar002');
             }
             break;
+        
+        case '/mongodb':
+            {
+                $manager = new MongoDB\Driver\Manager("mongodb://root:example@127.0.0.1:27017");
+                $command = new MongoDB\Driver\Command(['ping' => 1]);
+                $manager->executeCommand('admin', $command);
+            }
+            break;
 
         default:
             throw new DomainException("Unknown operation");