Hướng dẫn dùng denmark timezone trong PHP

Đôi lúc các bạn xem các hướng dẫn về hàm lấy thời gian :date(format,timestamp).

Nếu chưa biết hàm date(format,timestamp) bạn có thể xem ở link:http://congnghe5s.com/bai-viet/xu-ly-ngay-gio-trong-php.html.

Bạn thực hiện test lệnh trên máy và kết quả không khớp với time hiện tại. Đó là vì lí do thời gian hay timezone trên server bạn đã cài đặt khác vì thế để trả về đúng time bạn muốn bạn có thể dùng hàm:

Cú pháp:date_default_timezone_set ( string $timezone_identifier ).

tham số : $timezone_identifier bạn lấy ở đây:http://php.net/manual/en/timezones.asia.php

phù hợp khu vực bạn muốn ví dụ mình muốn set lấy giờ việt nam mình làm như sau:

date_default_timezone_set('Asia/Ho_Chi_Minh').

Sau đó các bạn dùng hàm : echo date('d/m/Y - H:i:s');

ra đúng kết quả time ở Việt Nam.

Ví dụ:

date_default_timezone_set('Asia/Ho_Chi_Minh');

if (date_default_timezone_get()) {
    echo 'date_default_timezone_set: ' . date_default_timezone_get() . '
';
}
echo date('d/m/Y H:i:s');

Ví dụ trên có 1 hàm cũng khá quan trọng : date_default_timezone_get()  trả về timezone mặc định của server bạn đang chạy hoặc là đã được thiết lập thông qua date_default_timezone_set().

Qua bài viết hy vọng các bạn có thêm kiến thức khi xử lý ngày tháng trong PHP.

I live in Denmark - but am setting up a page for a friend in USA (Washington State). The page is hosted at Surftown, in Denmark.

I know there is a 9 hour difference, so I set:

date_default_timezone_set('America/Los_Angeles');

But there is something I obviously don't quite understand about time zones / date() and strtotime() because:

Via text input I am trying to save a specific date and time to the database.

Lets say that $_POST[date] input is: '01/29/2015' and $_POST[time] input is: '02:00 PM'.

I then create a stamp using:

strtotime($_POST[date].' '.$_POST[time]);

But when I try to output this, I get the correct date - but 9 hours is added to time? Why is this?

I guess I could just remove the time zone setting for this specific task - but I'd like to understand why. I am setting the time zone because I also need to save some timestamps based on the actual time of the user (in Washington state - not Denmark).

Can you help?

Trong hướng dẫn này, chúng ta sẽ xem xét hai công cụ BDD khác nhau, Behat và phpspec, và xem chúng có thể hỗ trợ bạn như thế nào trong quá trình phát triển của bạn. Học BDD có thể gây nhầm lẫn. Phương pháp mới, công cụ mới và nhiều câu hỏi, chẳng hạn như "những gì cần kiểm tra?" và "công cụ nào để sử dụng?" Tôi hy vọng rằng ví dụ khá đơn giản này sẽ cung cấp cho bạn những ý tưởng về cách bạn có thể kết hợp BDD vào quy trình làm việc của riêng bạn.

Nguồn cảm hứng của tôi

Tôi đã có cảm hứng để viết hướng dẫn này bởi Taylor Otwell, người tạo ra khung công tác Laravel. Một vài lần, tôi đã nghe Taylor giải thích lý do tại sao anh ta hầu như không làm TDD / BDD bằng cách nói rằng anh ấy thích đầu tiên lên kế hoạch API của mã của mình, trước khi thực sự bắt đầu thực hiện nó. Tôi đã nghe điều này từ nhiều nhà phát triển, và mỗi khi tôi nghĩ đến bản thân mình: "Nhưng đó là trường hợp sử dụng hoàn hảo cho TDD / BDD!". Taylor nói rằng anh thích lập bản đồ API của mã của mình, bằng cách viết mã mà anh mong muốn. Sau đó, anh ta sẽ bắt đầu viết mã và không hài lòng cho đến khi đạt được API chính xác đó. Lập luận có ý nghĩa nếu bạn chỉ kiểm tra / chỉ định ở cấp độ đơn vị, nhưng sử dụng một công cụ như Behat, bạn bắt đầu với hành vi bên ngoài của phần mềm, về cơ bản, theo như tôi hiểu, những gì Taylor muốn hoàn thành.

Chúng tôi sẽ bao gồm những gì

Trong hướng dẫn này, chúng tôi sẽ xây dựng một lớp trình tải tệp cấu hình đơn giản. Chúng tôi sẽ bắt đầu bằng cách sử dụng cách tiếp cận của Taylor và sau đó chuyển sang một phương pháp BDD thay thế. Các ví dụ là tối giản, nhưng chúng tôi vẫn sẽ phải lo lắng về đồ đạc, phương pháp tĩnh, vv, vì vậy tất cả-trong-tất cả, tôi nghĩ rằng họ nên là đủ để hiển thị như thế nào và phpspec có thể bổ sung cho nhau.

Disclaimer: Trước hết, bài viết này không phải là một hướng dẫn bắt đầu. Nó giả định kiến ​​thức cơ bản về BDD, Behat và phpspec. Bạn có thể đã xem xét các công cụ này, nhưng vẫn đang vật lộn với cách sử dụng chúng thực sự trong công việc hàng ngày của bạn. Nếu bạn muốn đánh dấu trên phpspec, hãy xem hướng dẫn bắt đầu của tôi. Thứ hai, tôi đang sử dụng Taylor Otwell làm ví dụ. Tôi không biết gì về cách Taylor làm việc, bên cạnh những gì tôi nghe anh ta nói trong podcast vv Tôi sử dụng anh ta làm ví dụ vì anh ấy là một nhà phát triển tuyệt vời (anh ấy đã tạo Laravel!) Và vì anh ấy nổi tiếng. Tôi cũng có thể đã sử dụng một người khác, vì hầu hết các nhà phát triển, bao gồm cả bản thân tôi, không làm BDD tất cả các thời gian, được nêu ra. Ngoài ra, tôi không nói rằng công việc Taylor mô tả là xấu. Tôi nghĩ rằng đó là một ý tưởng tuyệt vời để đưa một số suy nghĩ vào mã của bạn trước khi thực sự viết nó. Hướng dẫn này chỉ nhằm mục đích thể hiện cách BDD để thực hiện điều này.

Luồng công việc của Taylor

Chúng ta hãy bắt đầu bằng cách xem Taylor có thể thiết kế bộ nạp tệp cấu hình này như thế nào. Taylor nói rằng anh thích chỉ tạo một tệp văn bản trống trong trình soạn thảo của mình và sau đó viết cách anh muốn các nhà phát triển có thể tương tác với mã của mình (API). Trong bối cảnh BDD, điều này thường được gọi là kiểm tra hành vi bên ngoài của phần mềm và các công cụ như Behat là điều tuyệt vời cho việc này. Chúng ta sẽ thấy điều này trong một thời gian ngắn.

Đầu tiên, có thể Taylor sẽ đưa ra quyết định về các tệp cấu hình. Họ nên làm việc như thế nào? Như trong Laravel, chúng ta hãy sử dụng các mảng PHP đơn giản. Một tệp cấu hình ví dụ có thể trông giống như sau:

# config.php

 'UTC',

);

Tiếp theo, mã nên sử dụng tệp cấu hình này hoạt động như thế nào? Hãy để chúng tôi làm điều này theo cách Taylor và chỉ viết mã mà chúng tôi mong muốn:

$config = Config::load('config.php');

$config->get('timezone'); // returns 'UTC'

$config->get('timezone', 'CET'); // returns 'CET' if 'timezone' is not configured

$config->set('timezone', 'GMT');
$config->get('timezone'); // returns 'GMT'

Được rồi, do đó, điều này có vẻ khá tốt. Đầu tiên chúng ta có một cuộc gọi tĩnh đến một hàm load (), tiếp theo là ba ca sử dụng:

  1. Lấy "múi giờ" từ tệp cấu hình.
  2. Nhận giá trị mặc định, nếu "múi giờ" chưa được định cấu hình.
  3. Thay đổi tùy chọn cấu hình bằng cách đặt nó thành một thứ khác. Chúng tôi sẽ mô tả từng trường hợp sử dụng, hoặc kịch bản, với Behat trong một thời gian ngắn.

Nó có ý nghĩa để sử dụng Behat cho những thứ này. Behat sẽ không ép buộc chúng tôi đưa ra bất kỳ quyết định thiết kế nào - chúng tôi có phpspec cho điều đó. Chúng tôi sẽ chỉ đơn giản là di chuyển các yêu cầu, mà chúng tôi vừa mô tả, thành một tính năng Behat để đảm bảo rằng chúng tôi làm đúng, khi chúng tôi bắt đầu xây dựng. Tính năng behat của chúng tôi sẽ phục vụ như là một thử nghiệm chấp nhận cho các yêu cầu của chúng tôi để nói chuyện.

Nếu bạn nhìn vào mã chúng tôi đã viết, một lý do khác để sử dụng Behat, thay vì chỉ phpspec, là cuộc gọi tĩnh. Nó không phải là dễ dàng để thử nghiệm các phương pháp tĩnh, đặc biệt là không nếu bạn chỉ sử dụng một công cụ như phpspec. Chúng ta sẽ thấy làm thế nào chúng ta có thể đi về điều này khi chúng ta có cả Behat và phpspec.

Thiết lập

Giả sử bạn đang sử dụng Composer, thiết lập Behat và phspec là một quá trình hai bước cực kỳ đơn giản.

Trước tiên, bạn cần một tệp composer.json cơ bản. Điều này bao gồm Behat và phpspec, và sử dụng psr-4 để tự động tải các lớp:

{
    "require-dev": {
        "behat/behat": "~3.0",
        "phpspec/phpspec": "~2.0"
    },
    "autoload": {
        "psr-4": {
            "": "src/"
        }
    }
}

Chạy cài đặt trình soạn nhạc để tìm nạp các phụ thuộc.

phpspec không cần bất kỳ cấu hình nào để chạy, trong khi Behat cần bạn chạy lệnh sau để tạo ra một giàn giáo cơ bản:

$ vendor/bin/behat --init

Đó là tất cả những gì mang theo. Điều này không thể là lý do của bạn vì không làm BDD!

Lập kế hoạch các tính năng với Behat

Vì vậy, bây giờ chúng ta đã thiết lập xong, chúng ta đã sẵn sàng để bắt đầu thực hiện BDD. Vì BDD có nghĩa là cài đặt và sử dụng Behat và phpspec, phải không?

Theo như tôi lo ngại, chúng tôi đã bắt đầu làm BDD. Chúng tôi đã mô tả hiệu quả hành vi bên ngoài của phần mềm của chúng tôi. "Khách hàng" của chúng tôi trong ví dụ này là các nhà phát triển, những người sẽ tương tác với mã của chúng tôi. Bởi "hiệu quả", tôi có nghĩa là chúng tôi đã mô tả hành vi theo cách họ sẽ hiểu. Chúng ta có thể lấy mã mà chúng ta đã phác thảo, đặt nó vào một tệp README, và mọi nhà phát triển PHP đều hiểu được việc sử dụng nó. Vì vậy, điều này là khá tốt thực sự, nhưng tôi có hai điều quan trọng cần lưu ý về điều này. Trước hết, mô tả hành vi của phần mềm sử dụng mã chỉ hoạt động trong ví dụ này bởi vì "khách hàng" là lập trình viên. Thông thường, chúng tôi đang thử nghiệm một cái gì đó sẽ được sử dụng bởi những người "bình thường". Một ngôn ngữ của con người là tốt hơn so với PHP khi chúng ta muốn giao tiếp với con người. Thứ hai, tại sao không tự động hóa điều này? Tôi sẽ không tranh luận tại sao điều này có thể là một ý tưởng hay.

Điều đó đang được nói, tôi nghĩ rằng bắt đầu sử dụng Behat bây giờ sẽ là một quyết định hợp lý.

Sử dụng Behat, chúng tôi muốn mô tả từng kịch bản mà chúng tôi đã nêu ở trên. Chúng tôi không muốn bao quát rộng rãi mọi trường hợp cạnh liên quan đến việc sử dụng phần mềm. Chúng tôi có phpspec có sẵn nếu điều này cần thiết để sửa lỗi trên đường đi, v.v. Tôi nghĩ rằng nhiều nhà phát triển, có thể bao gồm Taylor, cảm thấy như họ phải suy nghĩ mọi thứ qua và quyết định mọi thứ trước khi họ có thể viết các bài kiểm tra và thông số kỹ thuật. Đó là lý do tại sao họ chọn để bắt đầu mà không có BDD, bởi vì họ không muốn quyết định mọi thứ trước đó. Đây không phải là trường hợp với Behat, vì chúng ta đang mô tả hành vi và cách sử dụng bên ngoài. Để sử dụng Behat để mô tả một tính năng, chúng tôi không cần phải quyết định bất cứ điều gì nhiều hơn trong ví dụ trên bằng cách sử dụng một tập tin văn bản thô. Chúng tôi chỉ cần xác định các yêu cầu của tính năng - trong trường hợp này là API bên ngoài của lớp trình tải tệp cấu hình.

Bây giờ, chúng ta hãy lấy mã PHP ở trên và biến nó thành một tính năng Behat, sử dụng ngôn ngữ tiếng Anh (thực sự sử dụng ngôn ngữ Gherkin).

Tạo một tệp trong thư mục / feature, được gọi là config.feature và điền vào các trường hợp sau:

Feature: Configuration files
    In order to configure my application
    As a developer
    I need to be able to store configuration options in a file

    Scenario: Getting a configured option
        Given there is a configuration file
        And the option 'timezone' is configured to 'UTC'
        When I load the configuration file
        Then I should get 'UTC' as 'timezone' option

    Scenario: Getting a non-configured option with a default value
        Given there is a configuration file
        And the option 'timezone' is not yet configured
        When I load the configuration file
        Then I should get default value 'CET' as 'timezone' option

    Scenario: Setting a configuration option
        Given there is a configuration file
        And the option 'timezone' is configured to 'UTC'
        When I load the configuration file
        And I set the 'timezone' configuration option to 'GMT'
        Then I should get 'GMT' as 'timezone' option

Trong tính năng này, chúng tôi mô tả, từ bên ngoài, cách "nhà phát triển" có thể lưu trữ các tùy chọn cấu hình. Chúng tôi không quan tâm đến hành vi nội bộ - chúng tôi sẽ bắt đầu sử dụng phpspec. Miễn là tính năng này đang chạy màu xanh lá cây, chúng tôi không quan tâm những gì xảy ra đằng sau hậu trường.

Hãy để chúng tôi chạy Behat và xem những gì nó nghĩ về tính năng của chúng tôi:

$ vendor/bin/behat --dry-run --append-snippets 

Với lệnh này, chúng tôi yêu cầu Behat thêm các định nghĩa bước cần thiết vào ngữ cảnh tính năng của chúng tôi. Tôi sẽ không đi sâu vào chi tiết về Behat, nhưng điều này bổ sung thêm một loạt các phương thức trống vào lớp FeatureContext của chúng ta để ánh xạ tới các bước tính năng của chúng ta ở trên.

Ví dụ, hãy xem định nghĩa bước mà Behat đã thêm cho có một bước tệp cấu hình mà chúng tôi sử dụng làm bước "Đã cho" trong cả ba trường hợp:

/**
 * @Given there is a configuration file
 */
public function thereIsAConfigurationFile()
{
    throw new PendingException();
}

Bây giờ, tất cả những gì chúng ta phải làm là điền vào một số mã để mô tả điều này.

Định nghĩa bước viết

Trước khi chúng tôi bắt đầu, tôi có hai điểm quan trọng để thực hiện về các định nghĩa bước:

  1. Vấn đề là để chứng minh rằng hành vi bây giờ, không phải là chúng ta muốn nó được. Sau đó, chúng tôi có thể bắt đầu thiết kế mã của chúng tôi, hầu hết thời gian sử dụng phpspec, để có được màu xanh lá cây.
  2. Việc thực hiện các định nghĩa bước không quan trọng - chúng ta chỉ cần một cái gì đó hoạt động và đạt được "1". Chúng ta có thể cấu trúc lại sau.

Nếu bạn chạy vendor / bin / behat, bạn sẽ thấy rằng tất cả các kịch bản bây giờ có các bước đang chờ xử lý.

Chúng ta bắt đầu với bước đầu tiên Do có một tệp cấu hình. Chúng ta sẽ sử dụng một fixture của file cấu hình, vì vậy chúng ta có thể sử dụng phương thức static load () sau này. Chúng ta quan tâm đến phương thức static load () vì nó cung cấp cho chúng ta một API Config :: load () tốt, giống như Laravel Facades. Bước này có thể được thực hiện theo nhiều cách. Bây giờ, tôi nghĩ chúng ta chỉ cần đảm bảo rằng chúng ta có sẵn fixture và nó chứa một mảng:

/**
 * @Given there is a configuration file
 */
public function thereIsAConfigurationFile()
{
    if ( ! file_exists('fixtures/config.php'))
        throw new Exception("File 'fixtures/config.php' not found");

    $config = include 'fixtures/config.php';

    if ( ! is_array($config))
        throw new Exception("File 'fixtures/config.php' should contain an array");
}

Chúng ta sẽ nhận được màu xanh lá cây với bước này mà không cần thực hiện bất kỳ mã nào ngoài việc thực hiện các trận đấu. Mục đích của bước Given là đặt hệ thống ở trạng thái đã biết. Trong trường hợp này, điều đó có nghĩa là đảm bảo chúng tôi có tệp cấu hình.

Để đến bước xanh đầu tiên của chúng ta, chúng ta chỉ cần tạo ra vật cố định:

# fixtures/config.php

Tiếp theo, chúng ta có một bước Và, trong trường hợp này chỉ là một bí danh cho Given. Chúng tôi muốn đảm bảo rằng tệp cấu hình chứa tùy chọn cho múi giờ. Một lần nữa, điều này chỉ liên quan đến lịch thi đấu của chúng tôi, vì vậy chúng tôi không quan tâm nhiều về nó. Tôi đã đóng mã (hackish) sau đây lại với nhau để thực hiện điều này:

/**
 * @Given the option :option is configured to :value
 */
public function theOptionIsConfiguredTo($option, $value)
{
    $config = include 'fixtures/config.php';

    if ( ! is_array($config)) $config = [];

    $config[$option] = $value;

    $content = "

Đoạn mã trên không đẹp, nhưng nó hoàn thành những gì nó cần. Nó cho phép chúng tôi thao tác vật cố của mình từ bên trong tính năng của chúng tôi. Nếu bạn chạy Behat, bạn sẽ thấy rằng nó đã thêm tùy chọn "múi giờ" vào lịch thi đấu config.php:

 'UTC',
);

Bây giờ là lúc để mang lại một số "mã Taylor" gốc! Bước Khi tôi tải tệp cấu hình sẽ bao gồm mã mà chúng tôi thực sự quan tâm. Chúng tôi sẽ đưa vào một số mã từ tệp văn bản thô từ trước đó và đảm bảo rằng nó chạy:

/**
 * @When I load the configuration file
 */
public function iLoadTheConfigurationFile()
{
    $this->config = Config::load('fixtures/config.php'); // Taylor!
}

Chạy Behat, tất nhiên điều này sẽ thất bại, vì Config chưa tồn tại. Hãy để chúng tôi mang phpspec đến cứu!

Thiết kế với Phpspec

Khi chúng ta chạy Behat, chúng ta sẽ gặp lỗi nghiêm trọng:

PHP Fatal error: Class 'Config' not found...

Rất may, chúng tôi có phpspec, bao gồm các trình tạo mã tuyệt vời của nó:

$  vendor/bin/phpspec desc "Config"              
Specification for Config created in .../spec/ConfigSpec.php.

$ vendor/bin/phpspec run --format=pretty
Do you want me to create `Config` for you? y

$ vendor/bin/phpspec run --format=pretty

      Config

  10  ✔ is initializable


1 specs
1 examples (1 passed)
7ms

Với các lệnh này, phpspec đã tạo hai tệp sau cho chúng tôi:

spec/
`-- ConfigSpec.php
src/
`-- Config.php

Điều này giúp chúng tôi loại bỏ lỗi nghiêm trọng đầu tiên, nhưng Behat vẫn không chạy:

PHP Fatal error: Call to undefined method Config::load() in ...

load () sẽ là một phương thức tĩnh và như vậy, không dễ dàng được xác định bằng phpspec. Vì hai lý do, điều này là OK, mặc dù:

  1. Hành vi của phương thức load () sẽ rất đơn giản. Nếu chúng ta cần phức tạp hơn sau này, chúng ta có thể trích xuất logic thành các phương thức có thể thử nghiệm nhỏ.
  2. Hành vi, như bây giờ, được bao phủ đủ bởi Behat. Nếu phương pháp không tải tập tin vào một mảng đúng, Behat sẽ squirk vào chúng tôi.

Đây là một trong những tình huống mà rất nhiều nhà phát triển sẽ đánh vào tường. Họ sẽ vứt bỏ phpspec và kết luận rằng nó hút và đang chống lại họ. Nhưng, xem Behat và phpspec độc đáo bổ sung cho nhau ở đây như thế nào?

Thay vì cố gắng để có được mức độ phù hợp 100% với phpspec, chúng ta hãy chỉ thực hiện một hàm load () đơn giản và tự tin rằng nó được bao phủ bởi Behat:

settings = array();
    }

    public static function load($path)
    {
        $config = new static();

        if (file_exists($path))
            $config->settings = include $path;

        return $config;
    }
}

Chúng tôi khá tự tin rằng các tùy chọn cấu hình của chúng tôi hiện đã được tải. Nếu không, các bước còn lại của chúng tôi sẽ không thành công và chúng tôi có thể xem xét lại điều này.

Xây dựng tính năng với sự lặp lại

Trở lại màu xanh lá cây với cả Behat và phpspec, bây giờ chúng ta có thể xem xét bước tính năng tiếp theo của chúng tôi Sau đó, tôi sẽ nhận được 'UTC' là tùy chọn 'múi giờ'.

/**
 * @Then I should get :value as :option option
 */
public function iShouldGetAsOption($value, $option)
{
    $actual = $this->config->get($option); // Taylor!

    if ( ! strcmp($value, $actual) == 0)
        throw new Exception("Expected {$actual} to be '{$option}'.");
}

Trong bước này, chúng tôi viết thêm mã đó mà chúng tôi mong muốn. Chạy Behat, chúng ta sẽ thấy rằng chúng ta không có phương thức get ():

PHP Fatal error: Call to undefined method Config::get() in ...

Đã đến lúc quay lại phpspec và sắp xếp nó ra.

Thử nghiệm truy cập và mutators, AKA getters và setters, là gần như giống như gà cũ hoặc dillemma trứng. Làm thế nào chúng ta có thể kiểm tra phương thức get () nếu chúng ta chưa có phương thức set () và ngược lại. Làm thế nào tôi có xu hướng đi về điều này là chỉ cần kiểm tra cả hai cùng một lúc. Điều này có nghĩa là chúng tôi sẽ thực hiện chức năng để thiết lập tùy chọn cấu hình, mặc dù chúng tôi chưa đạt được kịch bản đó.

Ví dụ sau nên làm:

function it_gets_and_sets_a_configuration_option()
{
    $this->get('foo')->shouldReturn(null);

    $this->set('foo', 'bar');

    $this->get('foo')->shouldReturn('bar');
}

Đầu tiên, chúng ta sẽ có máy phát điện phpspec giúp chúng ta bắt đầu:

$ vendor/bin/phpspec run --format=pretty
Do you want me to create `Config::get()` for you? y

$ vendor/bin/phpspec run --format=pretty
Do you want me to create `Config::set()` for you? y

$ vendor/bin/phpspec run --format=pretty

      Config

  10  ✔ is initializable
  15  ✘ gets and sets a configuration option
        expected "bar", but got null.

        ...

1 specs
2 examples (1 passed, 1 failed)
9ms

Bây giờ, chúng ta hãy trở lại với màu xanh lá cây:

public function get($option)
{
    if ( ! isset($this->settings[$option]))
        return null;

    return $this->settings[$option];
}

public function set($option, $value)
{
    $this->settings[$option] = $value;
}

Và, chúng tôi đi:

$ vendor/bin/phpspec run --format=pretty

      Config

  10  ✔ is initializable
  15  ✔ gets and sets a configuration option


1 specs
2 examples (2 passed)
9ms

Điều đó đã cho chúng ta một chặng đường dài. Chạy Behat, chúng ta thấy rằng chúng ta đang ở trong kịch bản thứ hai bây giờ. Tiếp theo, chúng ta cần triển khai tính năng tùy chọn mặc định, vì get () chỉ trả về null ngay bây giờ.

Bước tính năng đầu tiên tương tự như bước chúng tôi đã viết trước đó. Thay vì thêm tùy chọn vào mảng, chúng tôi sẽ bỏ tùy chọn đó:

/**
 * @Given the option :option is not yet configured
 */
public function theOptionIsNotYetConfigured($option)
{
    $config = include 'fixtures/config.php';

    if ( ! is_array($config)) $config = [];

    unset($config[$option]);

    $content = "

Đây không phải là đẹp. Tôi biết! Chúng ta chắc chắn có thể tái cấu trúc nó, vì chúng ta lặp lại chính mình, nhưng đó không phải là phạm vi của hướng dẫn này.

Bước tính năng thứ hai cũng có vẻ quen thuộc và chủ yếu là sao chép và dán từ trước đó:

/**
 * @Then I should get default value :default as :option option
 */
public function iShouldGetDefaultValueAsOption($default, $option)
{
    $actual = $this->config->get($option, $default); // Taylor!

    if ( ! strcmp($default, $actual) == 0)
        throw new Exception("Expected {$actual} to be '{$default}'.");
}

get () trả về null. Chúng ta hãy chuyển sang phpspec và viết một ví dụ để giải quyết vấn đề này:

function it_gets_a_default_value_when_option_is_not_set()
{
    $this->get('foo', 'bar')->shouldReturn('bar');

    $this->set('foo', 'baz');

    $this->get('foo', 'bar')->shouldReturn('baz');
}

Đầu tiên, chúng tôi kiểm tra rằng chúng tôi nhận được giá trị mặc định nếu "tùy chọn" chưa được định cấu hình. Thứ hai, chúng tôi đảm bảo rằng tùy chọn mặc định không ghi đè tùy chọn được định cấu hình.

Thoạt nhìn, phpspec có vẻ như quá mức trong trường hợp này, vì chúng tôi gần như đang thử nghiệm cùng một điều với Behat. Tôi thích sử dụng phpspec để xác định các trường hợp cạnh, đó là loại ngụ ý trong kịch bản. Ngoài ra, các trình tạo mã của phpspec thực sự tuyệt vời. Tôi sử dụng chúng cho tất cả mọi thứ và tôi thấy mình làm việc nhanh hơn bất cứ khi nào tôi đang sử dụng phpspec.

Bây giờ, phpspec xác nhận những gì Behat đã nói với chúng tôi:

$ vendor/bin/phpspec run --format=pretty

      Config

  10  ✔ is initializable
  15  ✔ gets and sets a configuration option
  24  ✘ gets a default value when option is not set
        expected "bar", but got null.

        ...

1 specs
3 examples (2 passed, 1 failed)
9ms

Để quay trở lại màu xanh lá cây, chúng ta sẽ thêm "trả lại sớm" vào phương thức get ():

public function get($option, $defaultValue = null)
{
    if ( ! isset($this->settings[$option]) and ! is_null($defaultValue))
        return $defaultValue;

    if ( ! isset($this->settings[$option]))
        return null;

    return $this->settings[$option];
}

Chúng tôi thấy rằng phpspec hiện đang hạnh phúc:

$ vendor/bin/phpspec run --format=pretty

      Config

  10  ✔ is initializable
  15  ✔ gets and sets a configuration option
  24  ✔ gets a default value when option is not set


1 specs
3 examples (3 passed)
9ms

Và như vậy là Behat, và chúng tôi cũng vậy.

Chúng tôi đang thực hiện với kịch bản thứ hai của chúng tôi và có một trái để đi. Đối với kịch bản cuối cùng, chúng tôi chỉ cần viết định nghĩa bước cho Và tôi đặt tùy chọn cấu hình 'múi giờ' thành bước 'GMT':

/**
 * @When I set the :option configuration option to :value
 */
public function iSetTheConfigurationOptionTo($option, $value)
{
    $this->config->set($option, $value); // Taylor!
}

Vì chúng ta đã thực hiện phương thức set (), bước này đã là màu xanh lá cây:

$ vendor/bin/behat                      
Feature: Configuration files
    In order to configure my application
    As a developer
    I need to be able to store configuration options in a file

  Scenario: Getting a configured option              # features/config.feature:6
    Given there is a configuration file              # FeatureContext::thereIsAConfigurationFile()
    And the option 'timezone' is configured to 'UTC' # FeatureContext::theOptionIsConfiguredTo()
    When I load the configuration file               # FeatureContext::iLoadTheConfigurationFile()
    Then I should get 'UTC' as 'timezone' option     # FeatureContext::iShouldGetAsOption()

  Scenario: Getting a non-configured option with a default value # features/config.feature:12
    Given there is a configuration file                          # FeatureContext::thereIsAConfigurationFile()
    And the option 'timezone' is not yet configured              # FeatureContext::theOptionIsNotYetConfigured()
    When I load the configuration file                           # FeatureContext::iLoadTheConfigurationFile()
    Then I should get default value 'CET' as 'timezone' option   # FeatureContext::iShouldGetDefaultValueAsOption()

  Scenario: Setting a configuration option                 # features/config.feature:18
    Given there is a configuration file                    # FeatureContext::thereIsAConfigurationFile()
    And the option 'timezone' is configured to 'UTC'       # FeatureContext::theOptionIsConfiguredTo()
    When I load the configuration file                     # FeatureContext::iLoadTheConfigurationFile()
    And I set the 'timezone' configuration option to 'GMT' # FeatureContext::iSetTheConfigurationOptionTo()
    Then I should get 'GMT' as 'timezone' option           # FeatureContext::iShouldGetAsOption()

3 scenarios (3 passed)
13 steps (13 passed)
0m0.04s (8.92Mb)

Tổng kết

Mọi thứ đều tốt đẹp và xanh lá cây, vì vậy chúng ta hãy nhanh chóng kết thúc và xem những gì chúng tôi đã đạt được.

Chúng tôi đã mô tả một cách hiệu quả hành vi bên ngoài của trình tải tệp cấu hình, trước tiên bằng cách sử dụng cách tiếp cận của Taylor và sau đó bằng cách sử dụng phương pháp BDD truyền thống. Tiếp theo, chúng tôi đã triển khai tính năng này, sử dụng phpspec để thiết kế và mô tả hành vi nội bộ. Ví dụ chúng tôi đã làm việc trên là khá đơn giản, nhưng chúng tôi đã bao gồm những điều cơ bản. Nếu chúng ta cần phức tạp hơn, chúng ta có thể mở rộng những gì chúng ta đã có. Sử dụng BDD, chúng tôi có ít nhất ba tùy chọn:

  1. Nếu chúng ta quan sát lỗi hoặc cần phải thay đổi một số phần mềm bên trong của phần mềm, chúng ta có thể mô tả bằng cách sử dụng phpspec. Viết một ví dụ không hiển thị lỗi và viết mã cần thiết để chuyển sang màu xanh lục.
  2. Nếu chúng ta cần thêm một ca sử dụng mới vào những gì chúng ta có, chúng ta có thể thêm một kịch bản vào config.feature. Sau đó chúng tôi có thể làm việc theo cách của chúng tôi qua từng bước, sử dụng Behat và phpspec.
  3. Nếu chúng ta cần thực hiện một tính năng mới, chẳng hạn như hỗ trợ các tệp cấu hình YAML, chúng ta có thể viết một tính năng hoàn toàn mới và bắt đầu lại, sử dụng cách tiếp cận mà chúng ta đã sử dụng trong suốt hướng dẫn này.

Với thiết lập cơ bản này, chúng tôi không có lý do gì để không viết một bài kiểm tra hoặc thông số lỗi, trước khi chúng tôi viết mã của chúng tôi. Những gì chúng tôi đã xây dựng hiện được bao phủ bởi các bài kiểm tra, điều này sẽ giúp bạn làm việc với nó dễ dàng hơn trong tương lai. Thêm vào đó, mã của chúng tôi cũng được ghi đầy đủ. Các trường hợp sử dụng dự định được mô tả bằng tiếng Anh đơn giản và các hoạt động bên trong được mô tả trong thông số kỹ thuật của chúng tôi. Hai điều này sẽ giúp các nhà phát triển khác hiểu và làm việc với codebase một cách dễ dàng.

Tôi hy vọng rằng hướng dẫn này giúp bạn hiểu rõ hơn cách BDD có thể được sử dụng trong ngữ cảnh PHP, với Behat và phpspec. Nếu bạn có bất kỳ câu hỏi hoặc ý kiến, xin vui lòng gửi chúng dưới đây trong phần ý kiến.

Cảm ơn bạn đã đọc!