-
Notifications
You must be signed in to change notification settings - Fork 136
Introducing GPSPoint Validator #18
Changes from 10 commits
a5f75b5
7e799b0
cc8f2bf
2fc221d
885bb3f
2431df0
e945b8e
8476007
e307e49
eccffb4
9c29075
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
<?php | ||
/** | ||
* Zend Framework (http://framework.zend.com/) | ||
* | ||
* @link http://github.com/zendframework/zf2 for the canonical source repository | ||
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) | ||
* @license http://framework.zend.com/license/new-bsd New BSD License | ||
*/ | ||
|
||
namespace Zend\Validator; | ||
|
||
final class GPSPoint extends AbstractValidator | ||
{ | ||
|
||
const OUT_OF_BOUNDS = 'gpsPointOutOfBounds'; | ||
const CONVERT_ERROR = 'gpsPointConvertError'; | ||
const INCOMPLETE_COORDINATE = 'gpsPointIncompleteCoordinate'; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
protected $messageTemplates = [ | ||
'gpsPointOutOfBounds' => '%value% is out of Bounds.', | ||
'gpsPointConvertError' => '%value% can not converted into a Decimal Degree Value.', | ||
'gpsPointIncompleteCoordinate' => '%value% did not provided a complete Coordinate', | ||
]; | ||
|
||
/** | ||
* Returns true if and only if $value meets the validation requirements | ||
* | ||
* If $value fails validation, then this method returns false, and | ||
* getMessages() will return an array of messages that explain why the | ||
* validation failed. | ||
* | ||
* @param mixed $value | ||
* @return bool | ||
* @throws Exception\RuntimeException If validation of $value is impossible | ||
*/ | ||
public function isValid($value) | ||
{ | ||
if (strpos($value, ',') === false) { | ||
$this->error(GPSPoint::INCOMPLETE_COORDINATE, $value); | ||
return false; | ||
} | ||
|
||
list($lat, $long) = explode(',', $value); | ||
|
||
if ($this->isValidCoordinate($lat, 90.0000) && $this->isValidCoordinate($long, 180.000)) { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* @param string $value | ||
* @param $maxBoundary | ||
* @return bool | ||
*/ | ||
private function isValidCoordinate($value, $maxBoundary) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why this is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @freax We've been gradually moving towards private visibility by default. This encourages good encapsulation, and moves the onus for justifying protected/public visibility to consumers; in other words, we need a very, very good reason to make something visible to extension writers. Doing this eases our maintenance burden, because we can add, change, or remove private methods with little to no impact on end-users. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just look around a whole framework and you hardly notice a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, this is the direction we're moving. While we've defaulted to
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @freax have a read at http://ocramius.github.io/blog/when-to-declare-classes-final/ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, it's pretty obvious. But my question is why need to forbid extend class from framework, especially from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
See again http://ocramius.github.io/blog/when-to-declare-classes-final/ (2. Encouraging composition) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, if treat the whole article as the ultimate truth... |
||
{ | ||
$this->value = $value; | ||
|
||
$value = $this->removeWhiteSpace($value); | ||
if ($this->isDMSValue($value)) { | ||
$value = $this->convertValue($value); | ||
} else { | ||
$value = $this->removeDegreeSign($value); | ||
} | ||
|
||
if ($value === false || $value === null) { | ||
$this->error(self::CONVERT_ERROR); | ||
return false; | ||
} | ||
|
||
$doubleLatitude = (double)$value; | ||
|
||
if ($doubleLatitude <= $maxBoundary && $doubleLatitude >= $maxBoundary * -1) { | ||
return true; | ||
} | ||
|
||
$this->error(self::OUT_OF_BOUNDS); | ||
return false; | ||
} | ||
|
||
/** | ||
* Determines if the give value is a Degrees Minutes Second Definition | ||
* | ||
* @param $value | ||
* @return bool | ||
*/ | ||
private function isDMSValue($value) | ||
{ | ||
return preg_match('/([°\'"]+[NESW])/', $value) > 0; | ||
} | ||
|
||
|
||
/** | ||
* @param string $value | ||
* @return bool|string | ||
*/ | ||
private function convertValue($value) | ||
{ | ||
$matches = []; | ||
$result = preg_match_all('/(\d{1,3})°(\d{1,2})\'(\d{1,2}[\.\d]{0,6})"[NESW]/i', $value, $matches); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
if ($result === false || $result === 0) { | ||
return false; | ||
} | ||
|
||
return $matches[1][0] + $matches[2][0]/60 + ((double)$matches[3][0])/3600; | ||
} | ||
|
||
/** | ||
* @param string $value | ||
* @return string | ||
*/ | ||
private function removeWhiteSpace($value) | ||
{ | ||
return preg_replace('/\s/', '', $value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep. My point is that very unlikely there is need to check coordinate value in format like 34' 23" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A line break is very unlikely - that's right. But a |
||
} | ||
|
||
/** | ||
* @param string $value | ||
* @return string | ||
*/ | ||
private function removeDegreeSign($value) | ||
{ | ||
return str_replace('°', '', $value); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<?php | ||
/** | ||
* Zend Framework (http://framework.zend.com/) | ||
* | ||
* @link http://github.com/zendframework/zf2 for the canonical source repository | ||
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) | ||
* @license http://framework.zend.com/license/new-bsd New BSD License | ||
*/ | ||
|
||
namespace ZendTest\Validator; | ||
|
||
use Zend\Validator\GPSPoint; | ||
|
||
|
||
/** | ||
* @group Zend_Validator | ||
*/ | ||
class GPSPointTest extends \PHPUnit_Framework_TestCase | ||
{ | ||
|
||
/** | ||
* @var GPSPoint | ||
*/ | ||
protected $validator; | ||
|
||
public function setUp() | ||
{ | ||
$this->validator = new GPSPoint(); | ||
} | ||
|
||
|
||
/** | ||
* @dataProvider basicDataProvider | ||
* @covers \Zend\Validator\GPSPoint::isValid | ||
*/ | ||
public function testBasic($gpsPoint) | ||
{ | ||
$this->assertTrue($this->validator->isValid($gpsPoint)); | ||
} | ||
|
||
/** | ||
* @covers \Zend\Validator\GPSPoint::isValid | ||
*/ | ||
public function testBoundariesAreRespected() | ||
{ | ||
$this->assertFalse($this->validator->isValid('181.8897,-77.0089')); | ||
$this->assertFalse($this->validator->isValid('38.8897,-181.0089')); | ||
$this->assertFalse($this->validator->isValid('-181.8897,-77.0089')); | ||
$this->assertFalse($this->validator->isValid('38.8897,181.0089')); | ||
} | ||
|
||
/** | ||
* @covers \Zend\Validator\GPSPoint::isValid | ||
* @dataProvider ErrorMessageTestValues | ||
*/ | ||
public function testErrorsSetOnOccur($value, $messageKey, $messageValue) | ||
{ | ||
$this->assertFalse($this->validator->isValid($value)); | ||
$messages = $this->validator->getMessages(); | ||
$this->assertArrayHasKey($messageKey, $messages); | ||
$this->assertContains($messageValue, $messages[$messageKey]); | ||
} | ||
|
||
public function basicDataProvider() | ||
{ | ||
return [ | ||
['38° 53\' 23" N, 77° 00\' 32" W'], | ||
['15° 22\' 20.137" S, 35° 35\' 14.686" E'], | ||
['65° 4\' 36.434" N,-22.728867530822754'], | ||
['38.8897°, -77.0089°'], | ||
['38.8897,-77.0089'] | ||
]; | ||
} | ||
|
||
public function ErrorMessageTestValues() | ||
{ | ||
return [ | ||
['63 47 24.691 N, 18 2 54.363 W', GPSPoint::OUT_OF_BOUNDS, '63 47 24.691 N'], | ||
['° \' " N,° \' " E', GPSPoint::CONVERT_ERROR, '° \' " N'], | ||
['° \' " N', GPSPoint::INCOMPLETE_COORDINATE, '° \' " N'], | ||
]; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this is
final
class?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See Link from @manuakasam in Comments below.