Giter Club home page Giter Club logo

framework's Introduction

PhpDeal

Design by Contract framework for PHP

What is Design by Contract?

The specification of a class or interface is the collection of non-private items provided as services to the caller, along with instructions for their use, as stated in phpDoc. Design By Contract is an effective technology for creating a specification.

The fundamental idea of Design By Contract is to treat the services offered by a class or interface as a contract between the class (or interface) and its caller. Here, the word "contract" is meant to convey a kind of formal, unambiguous agreement between two parties:

  • requirements upon the caller made by the class
  • promises made by the class to the caller

If the caller fulfills the requirements, then the class promises to deliver some well-defined service. Some changes to a specification/contract will break the caller, and some won't. For determining if a change will break a caller, C++ FAQs uses the memorable phrase "require no more, promise no less": if the new specification does not require more from the caller than before, and if it does not promise to deliver less than before, then the new specification is compatible with the old, and will not break the caller.

Build Status GitHub release Code Coverage Scrutinizer Code Quality Minimum PHP Version License

Installation

PhpDeal framework can be installed with composer. Installation is quite easy, just ask composer to download the framework with its dependencies by running the command:

$ composer require php-deal/framework

Setup

Put the following code at the beginning of your application entry point or require it from an external file.

$instance = ContractApplication::getInstance();
$instance->init(array(
    'debug'    => true,
    'appDir'   => __DIR__,
    'excludePaths' => [
        __DIR__ . '/vendor'
    ],
    'includePaths' => [

    ],
    'cacheDir' => __DIR__.'/cache/',
));

Symfony setup

Put the following code in app_dev.php and adapt it to match your folder structure. The appDir must point to the folder containing the src files, not the document root folder !

$instance = ContractApplication::getInstance();
$instance->init(array(
    'debug'    => true,
    'appDir'   => __DIR__ . '/../src',
    'excludePaths' => [

    ],
    'includePaths' => [

    ],
    'cacheDir' => __DIR__.'/var/cache/',
));

Pre and Post Contracts

The pre contracts specify the preconditions (requirements) before a statement is executed. The most typical use of this would be in validating the parameters to a function. The post contracts (promises) validate the result of the statement. The most typical use of this would be in validating the return value of a method and of any side effects it has. The syntax is:

<?php
namespace Vendor\Namespace;

use PhpDeal\Annotation as Contract; //import DbC annotations

/**
 * Some account class
 */
class Account
{

    /**
     * Current balance
     *
     * @var float
     */
    protected $balance = 0.0;

    /**
     * Deposits fixed amount of money to the account
     *
     * @param float $amount
     *
     * @Contract\Verify("$amount>0 && is_numeric($amount)")
     * @Contract\Ensure("$this->balance == $__old->balance+$amount")
     */
    public function deposit($amount)
    {
        $this->balance += $amount;
    }
}

By definition, if a pre contract fails, then the body received bad parameters. A ContractViolation exception is thrown. If a post contract fails, then there is a bug in the body. A ContractViolation exception is thrown.

Invariants

Invariants are used to specify characteristics of a class that always must be true (except while executing a protected or private member function).

The invariant is a contract saying that the asserts must hold true. The invariant is checked when a class constructor completes and at the end of the class public methods:

<?php
namespace Vendor\Namespace;

use PhpDeal\Annotation as Contract; //import DbC annotations

/**
 * Some account class
 *
 * @Contract\Invariant("$this->balance > 0")
 */
class Account
{

    /**
     * Current balance
     *
     * @var float
     */
    protected $balance = 0.0;

    /**
     * Deposits fixed amount of money to the account
     *
     * @param float $amount
     */
    public function deposit($amount)
    {
        $this->balance += $amount;
    }
}

Invariants contain assert expressions, and so when they fail, they throw a ContractViolation exception.

NOTE: The code in the invariant may not call any public non-static members of the class, either directly or indirectly. Doing so will result in a stack overflow, as the invariant will wind up being called in an infinitely recursive manner.

Contract propagation

There a some differences in inheritance of the contracts:

  1. Ensure
  • if provided Ensure will automatically inherit all contracts from parent class or interface
  1. Verify
  • if provided Verify will not inherit contracts from parent class or interface
  • to inherit contracts you will need to provide @inheritdoc or the Inherit contract
  1. Invariant
  • if provided Invariant will inherit all contracts from parent class or interface
  1. Inherit
  • if provided Inherit will inherit all contracts from the given level (class, method) without the need to provide a contract on your current class or method

Notes:

  • The parsing of a contract only happens IF you provide any given annotation from this package. Without it, your contracts won't work!
  • The annotation must not have curly braces ({}) otherwise the annotation reader can't find them.
class Foo extends FooParent
{
    /**
     * @param int $amount
     * @Contract\Verify("$amount != 1")
     */
    public function bar($amount)
    {
        ...
    }
}
    
class FooParent
{
    /**
     * @param int $amount
     * @Contract\Verify("$amount != 2")
     */
    public function bar($amount)
    {
        ...
    }
}
    

Foo::bar accepts 2 literal as a parameter and does not accept 1.

With @inheritdoc:

class Foo extends FooParent
{
    /**
     * @param int $amount
     * @Contract\Verify("$amount != 1")
     * {@inheritdoc}
     */
    public function bar($amount)
    {
        ...
    }
}
    
class FooParent
{
    /**
     * @param int $amount
     * @Contract\Verify("$amount != 2")
     */
    public function bar($amount)
    {
        ...
    }
}
    

Foo::bar does not accept 1 and 2 literals as a parameter.

For postconditions (Ensure and Invariants contracts) subclasses inherit contracts and they don't need @inheritdoc. Example:

    
/**
 * @Contract\Invariant("$this->amount != 1")
 */
class Foo extends FooParent
{
    
}

/**
 * @Contract\Invariant("$this->amount != 2")
 */
class FooParent
{
    /**
     * @var int
     */
    protected $amount;
    
    /**
     * @param int $amount
     */
    protected function setBar($amount)
    {
        $this->amount = $amount;
    }
}
    

Foo::setBar does not accept 1 and 2 literals as a parameter.

If you don't want to provide a contract on your curent method/class you can use the Inherit annotation:

class Foo extends FooParent
{
    /**
     * @param int $amount
     * @Contract\Inherit
     */
    public function bar($amount)
    {
        ...
    }
}
    
class FooParent
{
    /**
     * @param int $amount
     * @Contract\Verify("$amount != 2")
     */
    public function bar($amount)
    {
        ...
    }
}

Foo:bar() does accept eveything, except: 2

Integration with assertion library

To enhance capabilities of contracts, it's possible to use assertion library.

    /**
     * Deposits fixed amount of money to the account
     *
     * @param float $amount
     *
     * @Contract\Ensure("Assert\Assertion::integer($this->balance)")
     */
    public function deposit($amount)
    {
        $this->balance += $amount;
    }

More assertions

IDE Integration

To improve your productivity with PhpStorm, you should definitely install a Go! AOP Framework plugin (>=1.0.1) to have a PHP syntax highlighting for defining contracts and navigation to AOP advices. PhpStorm example

Common issues

Fatal error: Uncaught Error: Class 'Go\ParserReflection\Instrument\PathResolver'
Fatal error: Uncaught Error: Class 'Go\ParserReflection\Instrument\PathResolver' 
not found in .../vendor/goaop/parser-reflection/src/ReflectionEngine.php on line XXX

This happens if your appDir configuration points at the same level as your vendor directory. To solve this issue try adding your vendor folder into the excludePaths configuration.

ContractApplication::getInstance()->init(array(
    'debug'    => true,
    'appDir'   => __DIR__,,
    'excludePaths' => [
        __DIR__ . '/vendor'
    ],
    'cacheDir' => __DIR__.'/cache/',
));

framework's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar icanhazstring avatar lcetinsoy avatar lisachenko avatar panosru avatar pdaw avatar peter279k avatar serafimarts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

framework's Issues

Add invariant support for classes

A class invariant is a property that each instance of the class is guaranteed to have when a method is called (like a common pre-condition for all methods), and that in return each method and constructor must ensure remains true when they terminate (like a common post-condition).

Library name in Scrutinizer and Packagist

To be consistent, scrutinizer-ci.com/g/lisachenko/php-deal and packagist.org/packages/lisachenko/php-deal should be renamed or a new project should be created :)

Bootstrap from class and run on self

Hello again!

Nice progress on the framework!

I run in an issue today, I wanted to have an "App" class for an application I'm building and there to have a bootstrap method (run), but when I tried to apply contracts on that same app it didn't work.

MyApp.php file:

<?php
declare(strict_types=1);

namespace Demo;

use PhpDeal\Annotation as Contract;
use PhpDeal\ContractApplication;

class MyApp
{
    /**
     * @return void
     */
    final public function run(): void
    {
      ContractApplication::getInstance()->init([
          'debug' => true,
          'appDir' => __DIR__ ,
          'cacheDir' => __DIR__ . '/../cache',
      ]);
    }

    /**
     * Just a number
     *
     * @param int $num
     *
     * @return void
     *
     * @Contract\Verify("$num>10 && is_numeric($num)")
     */
    public function test(int $num): void
    {
      echo $num;
    }
}

test file:

<?php
declare(strict_types=1);

/**
 * Author: panosru
 * Date: 2019-02-19
 * Time: 16:41
 */

require_once __DIR__ . '/vendor/autoload.php';

$app = new \Demo\MyApp();
$app->run();

$app->test(5);

I got 5 as a result instead of an exception for contract violation:
image

Thanks!

Defining Expression Handlers

Thanks @lisachenko its really a great help to understand and use this library. I have few questions.
1)- Where are the expression handlers defined or in other words which Class/File is handling the Expressions (@contract\Verify("$amount>0 && is_numeric($amount)"))
2)- How complex expressions we can specify?
3)- How i can introduce my custom expression handlers?

many thanks in advance

using with PHPUnit

Met a Fatal Error:

PHP Fatal error:  Cannot redeclare class PHP_TokenWithScope in /usr/share/nginx/yii-version/vendor/phpunit/php-token-stream/src/Token.php on line 171

Using Contracts with a PHPUnit

Invariant for magic getters

@Contract\Invariant("$this->some") does not work if some field will be taken via magic getter __get. Its very popular solution for ActiveRecord libraries, like Eloquent or Yii1\2 AR and AnalogueOrm (DataMapper).

Error:

ErrorException (E_WARNING)
array_combine(): Both parameters should have an equal number of elements.

In ~/src/Aspect/AbstractContractAspect.php on line 46

Issue with exception display on command line

Hi there, would appreciate a discussion regarding this issue, thanks. On master, ref a1e3bd9.

As a developer using php-deal I expect to see a ContractViolation exception on the command line when a contract fails when running a script through the cli. I expect the ContractViolation exception to have a message describing the contract condition that failed, and the file/line of the method that failed; with a stack trace of my program.

The issue is, when running through the cli (tested with PHP 5.5.9 and 7.1.10) I see a DomainException, which doesn't have a meaningful message, and has a stack trace of the internals of the phpdeal and goaop frameworks. This does not help me debug my program.

I have tried various versions of PHP with xdebug on/off with varying results.

Expected:

PHP Fatal error:  Uncaught exception 'PhpDeal\Exception\ContractViolation' with message 'Contract $this->balance > 0 violated for Demo\Account->deposit' in phpdeal/demo/Demo/Account.php:38
Stack trace:
#0 phpdeal/src/Aspect/InvariantCheckerAspect.php(57): PhpDeal\Aspect\AbstractContractAspect->ensureContracts(Object(Go\Aop\Framework\DynamicClosureMethodInvocation), Array, Object(Demo\Account), 'Demo\Account__A...', Array)
#1 phpdeal/vendor/goaop/framework/src/Aop/Framework/AroundInterceptor.php(34): PhpDeal\Aspect\InvariantCheckerAspect->invariantContract(Object(Go\Aop\Framework\DynamicClosureMethodInvocation))
#2 phpdeal/vendor/goaop/framework/src/Aop/Framework/DynamicClosureMethodInvocation.php(40): Go\Aop\Framework\AroundInterceptor->invoke(Object(Go\Aop\Framework\DynamicClosureMethodInvocation))
#3 phpdeal/src/Aspect/PostconditionChecke in phpdeal/demo/Demo/Account.php on line 38

Actual:

PHP Fatal error:  Uncaught exception 'DomainException' with message 'Invalid return value received from the assertion body, only boolean or void can be returned' in phpdeal/src/Aspect/AbstractContractAspect.php:86
Stack trace:
#0 phpdeal/src/Aspect/InvariantCheckerAspect.php(57): PhpDeal\Aspect\AbstractContractAspect->ensureContracts(Object(Go\Aop\Framework\DynamicClosureMethodInvocation), Array, Object(Demo\Account), 'Demo\Account__A...', Array)
#1 phpdeal/vendor/goaop/framework/src/Aop/Framework/AroundInterceptor.php(34): PhpDeal\Aspect\InvariantCheckerAspect->invariantContract(Object(Go\Aop\Framework\DynamicClosureMethodInvocation))
#2 phpdeal/vendor/goaop/framework/src/Aop/Framework/DynamicClosureMethodInvocation.php(40): Go\Aop\Framework\AroundInterceptor->invoke(Object(Go\Aop\Framework\DynamicClosureMethodInvocation))
#3 phpdeal/src/A in phpdeal/demo/Demo/Account.php on line 38

I've had some trouble trying to work this one out. I think it has something to do with the exception chaining. I've tried to create a simple test case to isolate the issue outside of the php-deal framework, but haven't been successful - my isolated tests with exception chaining always work as expected, with separate messages for each exception thrown. When running tests with the php-deal framework, the ContractViolation exception is always missing from the cli output.

When I step through the code, the ContractViolation appears to be created correctly, and I can catch it in the outer scope. It seems to be a problem with the way PHP is displaying the exceptions and stack trace.

To recreate the issue, apply the following patch, and run the demo:

diff --git a/demo/Demo/Account.php b/demo/Demo/Account.php
index 8116a03..1ea5a3f 100644
--- a/demo/Demo/Account.php
+++ b/demo/Demo/Account.php
@@ -36,7 +36,7 @@ class Account implements AccountContractInterface
      */
     public function deposit($amount)
     {
-        $this->balance += $amount;
+        $this->balance = 0;
     }
 
     /**

I can temporarily get things working as expected by disabling the exception chaining as follows:

diff --git a/src/Exception/ContractViolation.php b/src/Exception/ContractViolation.php
index a957d26..fb2fb05 100644
--- a/src/Exception/ContractViolation.php
+++ b/src/Exception/ContractViolation.php
@@ -32,6 +32,8 @@ class ContractViolation extends \LogicException
         $objName    = is_object($obj) ? get_class($obj) : $obj;
         $method     = $invocation->getMethod();
 
+        $previous = null;
+
         $message = "Contract {$contract} violated for {$objName}->{$method->name}";
         parent::__construct($message, 0, $previous);
 

Some questions:

  1. I don't understand why the exception chaining isn't working as it currently stands. Can you repeat the issue? Can you explain it?
  2. As a developer using php-deal, I would prefer to see framework errors separate to ContractViolation exceptions. Does it make sense to chain the exceptions in this way? To me it would seem better to catch the DomainException at Aspect/AbstractContractAspect.php:89, and throw a ContractViolation exception without chaining (the Domain Exception doesn't appear to convey any useful information to the developer).
  3. Should other Error or Exception types should be handled differently? It might make sense to have a different exception type in the case the syntax/parsing of the contract failed.
  4. On code review, it appears that catching \Error $internalError at Aspect/AbstractContractAspect.php:89 and chaining it in Aspect/AbstractContractAspect.php:91 will fail due to the type hint in Exception/ContractViolation.php:29. I didn't see a test case for that - Aspect/AbstractContractAspect.php:91 isn't included in the code coverage.

Contract propagation

I wonder if we could inherit contacts from parent classes.

For example, we define an interface with common contract and all children (without own contract) would be obligated to fulfills parent's requirements.

I'm not sure if it's possible with goaop/framework.

Issue running PHPUnit with composer

An issue with autoloading when using PHPUnit installed via composer:

$ php vendor/bin/phpunit 
PHP Fatal error:  Call to a member function addPsr4() on a non-object in phpdeal/tests/bootstrap.php on line 8

I'll submit a PR for this.

Setup with symfony and better doc

Hello !

I am trying to use this framework which I always found very interesting but a bit difficult to setup. It is not the first time I fail to set it up with Symfony. I was not able to make a simple contract fails. Then I tried to use the demo to see if it was working on my setup and it is a bit tedious to setup (but I succeed ! ).

I think that this project is very nice and that Design By Contract should have better recognition. I think that with a slightly better documentation your tool could be much easier to use and has more user. Its pities me that its potential remains untapped.

So my issue has two points :

  1. Can you help me for setting it up with symfony ? [edit : think I got it]
  2. Ease demo testing (very easy)

I am willing to do a PR with docs and example when I solve my use case (even doing a blog post if time someday).

  1. Symfony (3.4) setup

I tried to the following code in my app_dev.php file with an obvious contract fail and everything goes normally, even if I do put it in my app_dev.php. Any Hint ?

Contract in some subfolder of "src" folder :

namespace Blabla\Model;

use PhpDeal\Annotation as Contract;

/* 
 * @Contract\Invariant("$this->name > 1000")
 */
class MyClass {
   
    public $name;
   
    public function setName() {
        return $this->name = "coucou";
    }
}

in my app_dev.php :

<?php

ini_set('memory_limit', '512M');

use Symfony\Component\Debug\Debug;
use Symfony\Component\HttpFoundation\Request;
use PhpDeal\ContractApplication;

require __DIR__.'/../vendor/autoload.php';

$instance = ContractApplication::getInstance();
$instance->init(array(
    'debug'    => true,
    'appDir'   => __DIR__,
    'cacheDir' => __DIR__.'/../var/cache/phpdeal/',
    'includePaths' => array(
            __DIR__ . '/../src/'
        )
));

$backend = new Backend\Model\Backend();
$backend->setName(); // !! should fail !!

$kernel = new AppKernel('dev', true);
if (PHP_VERSION_ID < 70000) {
//    $kernel->loadClassCache();
}
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
  1. Easing Demo and docs

Regarding the demo it could be nice to move the files in demo into a sub folder "src" and ad
a composer json in the demo folder with the following values. So you just clone cd demo && composer install && php demo.php and works in one line :)

{
    "name": "phpdeal/demo",
    "authors": [
        {
            "name": "bla",
            "email": "bla"
        }
    ],
    "require": {
        "php-deal/framework": "^1.0"
    },
    "autoload": {
        "psr-4": {
            "Demo\\":"src/Demo"
        }
    }
}

Thanks in advance !

[edit] : showing the list of parsed classes and available contracts could be nice, looking in code now.
[edit2] : think symfony is ok, needed to change app path to DIR '/../' even though I had an explicit includePaths, can you explain why ?

Add initial documentation for the project

Need to describe how to use this framework for DbC programming. How to declare contracts, how to define pre- and post-conditions, how to verify results of method execution

Project status?

Hi Alexander,

I'm interested in this project, but having issues with the way the exceptions are being displayed. What's the status? No commits for 2 years?

If you're still working on it, I will add some more detail.

Cheers,
Luke.

PathResolver' not found

Hi,

today I tried to install php-deal and I had the following error:

Fatal error: Uncaught Error: Class 'Go\ParserReflection\Instrument\PathResolver' not found in /var/www/html/...../vendor/goaop/parser-reflection/src/ReflectionEngine.php on line 218

I solved it by excluding the vendor folder in the config as it is said here: Codeception/AspectMock#112

I added 'excludePaths' to configuration:
ContractApplication::getInstance()->init(array( 'debug' => true, 'appDir' => __DIR__, 'cacheDir' => __DIR__.'/cache/', 'excludePaths' => [__DIR__.'/vendor/'] ));

PhpDeal is great!

Feature : automatic unit test generation based from contracts

Hello !

It is me again haha. Imagine the following : you put some CDD assertions with phpdeal (or other tool) and you have some automatic unit tests generated automatically. Wouldn't that be so cool ?.

I got this idea from the Hoa\Praspel project which is a specification language usable for CDD. It is similar to phpdeal, albeit a bit more potent maybe. They developed a small plugin which generates automatic unit test from Praspel contracts into atoum cf link. Unfortunately this idea was not pushed more and the main author of Hoa stopped to work on it.

I find this idea fascinating with a lot of potential and I may try to implement it for phpunit. I wanted to have your thoughts about it to get feedbacks. I am considering two options :

Idea 1 : Simply port hoaproject/Contributions-atoum-praspelExtension to phpunit since atoum ... well was not successful

Pros :

  • No too difficult I would say for a first shot.
  • Praspel comptability with Realistic domain can let specify very precise contracts

Cons :

  • They do not have a CDD implementation like phpdeal which are useful to catch errors at system level.
  • No integration with phpdeal which "has more user" than Praspel.

Idea 2 : Build the same mechanism directly from phpdeal. Maybe we could integration RealDom into it

Pros :

  • You have CDD and PhpUnit automated tests
  • Integration with Praspel\RealisticDom or other things (like Faker ) could be added to enrich assertions

Cons :

  • The mechanism to sample inputs from Verify contracts would be complicated to build ? I do not know yet

So here are my questions :

  1. What are your thoughts about this idea, overall ?
  2. Would some people be interested to help if I started to build something ?
  3. Would this built as a separated repository or integrated in this one ? I guess Separated.
  4. Regarding implementation of idea 2, do you agree with the following ?

A generated test would do the following :
1. Samples inputs according the Verify contracts
2. Run phpunit assertion using Ensure contracts.
3. Also load phpdeal "normally" to have invariants contract checking for free during tests.

To do that, the following could be done :

Load a php file to test and for each public method:

  1. Generate input data
       1.1 Read Verify contracts with MethodConditionFetch class ?
       1.2 Sample input from it with some Sampler classes to be written.
       1.3 Generate code for input data
  2. Generate unit test assertion
       1.1 Read Ensure contracts with ??
       1.2 For each Ensure contract generate a test assertion.
  3. Generate the whole test code in phpunit (or other framework) with a class to be written.

As I write and specify the to-do list, I get it could be a bit difficult (how to construct the object to be tested automatically if it is complex). I will keep updating, the post as I progress in my thinking.

What do you think ?

edit : eris and property based testing are also relevant to the thinking

Best

Laurent

Request for Documentation/Help to use this library

Hi,

Although I am not an expert in PHP, also new to Design By Contract paradigm. I found this library very useful for DbC. but I could not found a comprehensive guidance or documentation for it.
My specific question is can I have its comprehensive documentation?
or at-least some preliminary guidence how I can define my own pre-conditions and post-conditions?

In demo files Account.php when I tried to introduce new methods but they did not follow the same pre-conditions that previous methods were following.

Many Thanks in advance

Samran

Violating contracts should throw ContractViolation

Source branch: 1.x

A violation with a contract should not throw a DomainException with

Fatal error: Uncaught DomainException: Invalid return value received from the assertion body, only boolean or void can be returned in deal/demo/Account.php on line 51

To reproduce this, simply change the demo Account::getBalance to return any value instead 100.


It should instead raise an ContractViolation explaining what contract was violated with what value.
Somthing like this:

PHP Fatal error:  Uncaught PhpDeal\Exception\ContractViolation: Contract $amount>0 && is_numeric($amount) violated with -1 for Demo\Account->deposit in XXX

Since this will change exceptions raised, this is assumed a breaking change.
To change this, one has to change the AbstractContractAspect inside ensureContracts. If the $invocationResult equals false, a ContractViolation should be raised.

Contract propagation bug?

I'm not sure if I'm doing something wrong but if I modify this:

/**
* Returns current balance
*
* @Contract\Ensure("$__result == $this->balance")
* @return float
*/
public function getBalance()
{
return $this->balance;
}

into this:

    /**
     * {@inheritdoc}
     */
    public function getBalance()
    {
        // return $this->balance;
        return 10;
    }

I should've receive the following error:

Fatal error: Uncaught DomainException: Invalid return value received from the assertion body, only boolean or void can be returned in deal/demo/Account.php on line 51

PhpDeal\Exception\ContractViolation: Contract $__result == $this->balance violated for Demo\Account->getBalance in deal/demo/Account.php on line 51

instead, I'm getting:

$ php demo.php
10

this:

    /**
     * Returns current balance
     *
     * @Contract\Ensure("$__result == $this->balance")
     * @return float
     */
    public function getBalance()
    {
        // return $this->balance;
        return 10;
    }

returns the above-mentioned error as expected.

ZF2 Module

Hi,

i implemented a ZF2-Module for php-deal using goaop-zf2-module.
Would be nice if you can link this inside you readme.

Maybe a new colab under "php-deal" would be nice.
So we can group everything together like under goaop.

php-deal/framework
php-deal/php-deal-zf2

As an example.
But we don't have to :)

Greets

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.