Преглед изворни кода

[Validator] Implemented @Ip constraint

Bernhard Schussek пре 14 година
родитељ
комит
39c9bf160e

+ 48 - 0
src/Symfony/Component/Validator/Constraints/Ip.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace Symfony\Component\Validator\Constraints;
+
+/*
+ * This file is part of the Symfony framework.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
+
+/**
+ * Validates that a value is a valid IP address
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
+ */
+class Ip extends \Symfony\Component\Validator\Constraint
+{
+    const V4 = '4';
+    const V6 = '6';
+    const ALL = 'all';
+
+    static protected $versions = array(
+        self::V4,
+        self::V6,
+        self::ALL,
+    );
+
+    public $version = self::V4;
+
+    public $message = 'This is not a valid IP address';
+
+    /**
+     * @inheritDoc
+     */
+    public function __construct($options = null)
+    {
+        parent::__construct($options);
+
+        if (!in_array($this->version, self::$versions)) {
+            throw new ConstraintDefinitionException(sprintf('The option "version" must be one of "%s"', implode('", "', self::$versions)));
+        }
+    }
+}

+ 103 - 0
src/Symfony/Component/Validator/Constraints/IpValidator.php

@@ -0,0 +1,103 @@
+<?php
+
+namespace Symfony\Component\Validator\Constraints;
+
+/*
+ * This file is part of the Symfony framework.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+use Symfony\Component\Validator\Exception\UnexpectedTypeException;
+
+/**
+ * Validates whether a value is a valid IP address
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
+ */
+class IpValidator extends ConstraintValidator
+{
+    /**
+     * @inheritDoc
+     */
+    public function isValid($value, Constraint $constraint)
+    {
+        if (null === $value || '' === $value) {
+            return true;
+        }
+
+        if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString()'))) {
+            throw new UnexpectedTypeException($value, 'string');
+        }
+
+        $value = (string)$value;
+        $valid = false;
+
+        if ($constraint->version == Ip::V4 || $constraint->version == Ip::ALL) {
+            $valid = $this->isValidV4($value);
+        }
+
+        if ($constraint->version == Ip::V6 || $constraint->version == Ip::ALL) {
+            $valid = $valid || $this->isValidV6($value);
+        }
+
+        if (!$valid) {
+            $this->setMessage($constraint->message, array('{{ value }}' => $value));
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Validates that a value is a valid IPv4 address
+     *
+     * @param string $value
+     */
+    protected function isValidV4($value)
+    {
+        if (!preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $value, $matches)) {
+            return false;
+        }
+
+        for ($i = 1; $i <= 4; ++$i) {
+            if ($matches[$i] > 255) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Validates that a value is a valid IPv6 address
+     *
+     * @param string $value
+     */
+    protected function isValidV6($value)
+    {
+        if (!preg_match('/^[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){1,5}((:[0-9a-fA-F]{0,4}){1,2}|:([\d\.]+))$/', $value, $matches)) {
+            return false;
+        }
+
+        // allow V4 addresses mapped to V6
+        if (isset($matches[4]) && !$this->isValidV4($matches[4])) {
+            return false;
+        }
+
+        // "::" is only allowed once per address
+        if (($offset = strpos($value, '::')) !== false) {
+            if (strpos($value, '::', $offset + 1) !== false) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}

+ 181 - 0
tests/Symfony/Tests/Component/Validator/Constraints/IpValidatorTest.php

@@ -0,0 +1,181 @@
+<?php
+
+namespace Symfony\Tests\Component\Validator;
+
+use Symfony\Component\Validator\Constraints\Ip;
+use Symfony\Component\Validator\Constraints\IpValidator;
+
+class IpValidatorTest extends \PHPUnit_Framework_TestCase
+{
+    protected $validator;
+
+    protected function setUp()
+    {
+        $this->validator = new IpValidator();
+    }
+
+    public function testNullIsValid()
+    {
+        $this->assertTrue($this->validator->isValid(null, new Ip()));
+    }
+
+    public function testEmptyStringIsValid()
+    {
+        $this->assertTrue($this->validator->isValid('', new Ip()));
+    }
+
+    public function testExpectsStringCompatibleType()
+    {
+        $this->setExpectedException('Symfony\Component\Validator\Exception\UnexpectedTypeException');
+
+        $this->validator->isValid(new \stdClass(), new Ip());
+    }
+
+    /**
+     * @dataProvider getValidIpsV4
+     */
+    public function testValidIpsV4($date)
+    {
+        $this->assertTrue($this->validator->isValid($date, new Ip(array(
+            'version' => Ip::V4,
+        ))));
+    }
+
+    public function getValidIpsV4()
+    {
+        return array(
+            array('0.0.0.0'),
+            array('255.255.255.255'),
+            array('123.45.67.178'),
+        );
+    }
+
+    /**
+     * @dataProvider getValidIpsV6
+     */
+    public function testValidIpsV6($date)
+    {
+        $this->assertTrue($this->validator->isValid($date, new Ip(array(
+            'version' => Ip::V6,
+        ))));
+    }
+
+    public function getValidIpsV6()
+    {
+        return array(
+            array('2001:0db8:85a3:0000:0000:8a2e:0370:7334'),
+            array('2001:0DB8:85A3:0000:0000:8A2E:0370:7334'),
+            array('2001:0Db8:85a3:0000:0000:8A2e:0370:7334'),
+            array('fe80:0000:0000:0000:0202:b3ff:fe1e:8329'),
+            array('fe80:0:0:0:202:b3ff:fe1e:8329'),
+            array('fe80::202:b3ff:fe1e:8329'),
+            array('0:0:0:0:0:0:0:0'),
+            array('::'),
+            array('0::'),
+            array('::0'),
+            array('0::0'),
+            // IPv4 mapped to IPv6
+            array('2001:0db8:85a3:0000:0000:8a2e:0.0.0.0'),
+            array('::0.0.0.0'),
+            array('::255.255.255.255'),
+            array('::123.45.67.178'),
+        );
+    }
+
+    /**
+     * @dataProvider getValidIpsAll
+     */
+    public function testValidIpsAll($date)
+    {
+        $this->assertTrue($this->validator->isValid($date, new Ip(array(
+            'version' => Ip::ALL,
+        ))));
+    }
+
+    public function getValidIpsAll()
+    {
+        return array_merge($this->getValidIpsV4(), $this->getValidIpsV6());
+    }
+
+    /**
+     * @dataProvider getInvalidIpsV4
+     */
+    public function testInvalidIpsV4($date)
+    {
+        $this->assertFalse($this->validator->isValid($date, new Ip(array(
+            'version' => Ip::V4,
+        ))));
+    }
+
+    public function getInvalidIpsV4()
+    {
+        return array(
+            array('0'),
+            array('0.0'),
+            array('0.0.0'),
+            array('256.0.0.0'),
+            array('0.256.0.0'),
+            array('0.0.256.0'),
+            array('0.0.0.256'),
+            array('-1.0.0.0'),
+            array('foobar'),
+        );
+    }
+
+    /**
+     * @dataProvider getInvalidIpsV6
+     */
+    public function testInvalidIpsV6($date)
+    {
+        $this->assertFalse($this->validator->isValid($date, new Ip(array(
+            'version' => Ip::V6,
+        ))));
+    }
+
+    public function getInvalidIpsV6()
+    {
+        return array(
+            array('z001:0db8:85a3:0000:0000:8a2e:0370:7334'),
+            array('fe80'),
+            array('fe80:8329'),
+            array('fe80:::202:b3ff:fe1e:8329'),
+            array('fe80::202:b3ff::fe1e:8329'),
+            // IPv4 mapped to IPv6
+            array('2001:0db8:85a3:0000:0000:8a2e:0370:0.0.0.0'),
+            array('::0.0'),
+            array('::0.0.0'),
+            array('::256.0.0.0'),
+            array('::0.256.0.0'),
+            array('::0.0.256.0'),
+            array('::0.0.0.256'),
+        );
+    }
+
+    /**
+     * @dataProvider getInvalidIpsAll
+     */
+    public function testInvalidIpsAll($date)
+    {
+        $this->assertFalse($this->validator->isValid($date, new Ip(array(
+            'version' => Ip::ALL,
+        ))));
+    }
+
+    public function getInvalidIpsAll()
+    {
+        return array_merge($this->getInvalidIpsV4(), $this->getInvalidIpsV6());
+    }
+
+    public function testMessageIsSet()
+    {
+        $constraint = new Ip(array(
+            'message' => 'myMessage'
+        ));
+
+        $this->assertFalse($this->validator->isValid('foobar', $constraint));
+        $this->assertEquals($this->validator->getMessageTemplate(), 'myMessage');
+        $this->assertEquals($this->validator->getMessageParameters(), array(
+            '{{ value }}' => 'foobar',
+        ));
+    }
+}