<?php
/**
* Validates string for suitability as a Perforce key name.
*
* By default disallows:
* - whitespace
* - purely numeric names
* - revision characters ('#', '@')
* - wildcards ('*', '...')
* - slashes ('/')
* - non-printable characters
* - leading minus ('-')
*
* By default allows, but can block:
* - percent character ('%')
* - positional specifiers ('%%x')
*
* @copyright 2011 Perforce Software. All rights reserved.
* @license Please see LICENSE.txt in top-level folder of this distribution.
* @version <release>/<patch>
*/
namespace P4\Validate;
class KeyName extends AbstractValidate
{
const INVALID_TYPE = 'invalidType';
const IS_EMPTY = 'isEmpty';
const IS_NUMERIC = 'isNumeric';
const HAS_WHITESPACE = 'hasSpaces';
const REVISION_CHARACTERS = 'revision';
const WILDCARDS = 'wildcards';
const LEADING_MINUS = 'leadingMinus';
const UNPRINTABLE_CHARACTERS = 'unprintable';
const SLASHES = 'slashes';
const COMMAS = 'commas';
const PERCENT = 'percent';
const POSITIONAL_SPECIFIERS = 'positional';
const RELATIVE = 'relative';
protected $allowLeadingDash = false; // DASHNUM
protected $allowPurelyNumeric = false; // NNEGNUM
protected $allowRevSpec = false; // REV
protected $allowSlashes = false; // SLASH
protected $allowRelative = true; // REL
protected $allowWildcard = false; // WILD
protected $allowPercent = true; // !NOTAPERCENT
protected $allowPositional = true; // !NOPERCENT
protected $allowCommas = true; // !NOCOMMA
protected $messageTemplates = array(
self::INVALID_TYPE => "Invalid type given.",
self::IS_EMPTY => "Is an empty string.",
self::IS_NUMERIC => "Purely numeric values are not allowed.",
self::HAS_WHITESPACE => "Whitespace is not permitted.",
self::REVISION_CHARACTERS => "Revision characters ('#', '@') are not permitted.",
self::WILDCARDS => "Wildcards ('*', '...') are not permitted.",
self::LEADING_MINUS => "First character cannot be minus ('-').",
self::UNPRINTABLE_CHARACTERS => "Unprintable characters are not permitted.",
self::SLASHES => "Slashes ('/') are not permitted.",
self::COMMAS => "Commas (',') are not permitted.",
self::PERCENT => "Percent ('%') is not permitted.",
self::POSITIONAL_SPECIFIERS => "Positional specifiers ('%%x') are not permitted.",
self::RELATIVE => "Relative paths are not permitted."
);
/**
* Checks if the given string is a valid perforce spec name.
*
* @param string|int $value spec name value to validate.
* @return boolean true if value is a valid spec name, false otherwise.
*/
public function isValid($value)
{
$this->set($value);
// permit ints if allowPurelyNumeric is true.
if ($this->allowPurelyNumeric && is_int($value)) {
$value = (string) $value;
}
// test for valid type.
if (!is_string($value)) {
$this->error(static::INVALID_TYPE);
return false;
}
// test for empty value.
if ($value === '') {
$this->error(static::IS_EMPTY);
return false;
}
// test for leading minus ('-') character.
if (!$this->allowLeadingDash && $value[0] === "-") {
$this->error(static::LEADING_MINUS);
return false;
}
// test for purely numeric name.
if (!$this->allowPurelyNumeric && preg_match('/^[0-9]+$/', $value)) {
$this->error(static::IS_NUMERIC);
return false;
}
// test for unprintable characters.
// 'isprint' defines printing characters an ASCII code greater than 0x1f, except 0x7f (DEL).
// technically, that would mean 0x80+ is invalid but the server explicitly lets high-bitted values pass.
if (preg_match('/[\x00-\x1F\x7f]/', $value)) {
$this->error(static::UNPRINTABLE_CHARACTERS);
return false;
}
// test for whitespace.
if (preg_match('/\s/', $value)) {
$this->error(static::HAS_WHITESPACE);
return false;
}
// test for revision characters.
if (!$this->allowRevSpec && preg_match('/@|#/', $value)) {
$this->error(static::REVISION_CHARACTERS);
return false;
}
// test for forward slash character.
if (!$this->allowSlashes && strpos($value, '/') !== false) {
$this->error(static::SLASHES);
return false;
}
// If relative paths aren't allowed the following are blocked:
// two or more slashes after the first character
// containing '/./'
// containing '/../'
// ending in a slash (in 2+ character string)
// ending in '/.'
// ending in '/..'
if (!$this->allowRelative && preg_match('#.+/$|.+//|/\./|/\.\./|.+/$|/\.$|/\.\.$#', $value)) {
$this->error(static::RELATIVE);
return false;
}
// test for wildcard characters.
if (!$this->allowWildcard && preg_match('/\*|\.\.\./', $value)) {
$this->error(static::WILDCARDS);
return false;
}
// test for percent character
if (!$this->allowPercent && strpos($value, '%') !== false) {
$this->error(static::PERCENT);
return false;
}
// test for positional specifiers.
if (!$this->allowPositional && strpos($value, '%%') !== false) {
$this->error(static::POSITIONAL_SPECIFIERS);
return false;
}
// test for comma character
if (!$this->allowCommas && strpos($value, ',') !== false) {
$this->error(static::COMMAS);
return false;
}
return true;
}
/**
* Control if a leading - character is permitted.
* Alias DASHNUM.
*
* @param bool $allowed pass true to allow leading dash, false (default) to disallow.
*/
public function allowLeadingDash($allowed)
{
$this->allowLeadingDash = (bool) $allowed;
}
/**
* Control if purely numeric key names are permitted
* (values consisting of only characters 0-9).
* Alias NNEGNUM.
*
* @param bool $allowed pass true to allow purely numeric names, false (default) to disallow.
*/
public function allowPurelyNumeric($allowed)
{
$this->allowPurelyNumeric = (bool) $allowed;
}
/**
* Control if revision specifiers are permitted (@ and # characters).
* Alias REV.
*
* @param bool $allowed pass true to allow rev specifiers, false (default) to disallow.
*/
public function allowRevSpec($allowed)
{
$this->allowRevSpec = (bool) $allowed;
}
/**
* Control if forward slashes '/' are permitted.
* Alias SLASH.
*
* @param bool $allowed pass true to allow forward slashes, false (default) to disallow.
*/
public function allowSlashes($allowed)
{
$this->allowSlashes = (bool) $allowed;
}
/**
* Control if forward relative paths are permitted (//, /., /./, /../, /.., trailing /)
* Alias REL.
*
* @param bool $allowed pass true (default) to allow relative, false to disallow.
*/
public function allowRelative($allowed)
{
$this->allowRelative = (bool) $allowed;
}
/**
* Control if wildcard sequences (* or ...) are permitted
* Alias WILD.
*
* @param bool $allowed pass true to allow positional specifiers, false (default) to disallow.
*/
public function allowWildcard($allowed)
{
$this->allowWildcard = (bool) $allowed;
}
/**
* Control if percent character '%' is permitted
* Alias !NOTAPERCENT.
*
* @param bool $allowed pass true (default) to allow percent '%', false to disallow.
*/
public function allowPercent($allowed)
{
$this->allowPercent = (bool) $allowed;
}
/**
* Control if positional sequence '%%' is permitted
* Alias !NOPERCENT.
*
* @param bool $allowed pass true (default) to allow percent '%%', false to disallow.
*/
public function allowPositional($allowed)
{
$this->allowPositional = (bool) $allowed;
}
/**
* Control if comma character ',' is permitted
* Alias !NOCOMMA.
*
* @param bool $allowed pass true (default) to allow commas ',', false to disallow.
*/
public function allowCommas($allowed)
{
$this->allowCommas = (bool) $allowed;
}
}