Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F1432091
MultiConstraint.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
MultiConstraint.php
View Options
<?php
/*
* This file is part of composer/semver.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace
Composer\Semver\Constraint
;
/**
* Defines a conjunctive or disjunctive set of constraints.
*/
class
MultiConstraint
implements
ConstraintInterface
{
/**
* @var ConstraintInterface[]
* @phpstan-var non-empty-array<ConstraintInterface>
*/
protected
$constraints
;
/** @var string|null */
protected
$prettyString
;
/** @var string|null */
protected
$string
;
/** @var bool */
protected
$conjunctive
;
/** @var Bound|null */
protected
$lowerBound
;
/** @var Bound|null */
protected
$upperBound
;
/**
* @param ConstraintInterface[] $constraints A set of constraints
* @param bool $conjunctive Whether the constraints should be treated as conjunctive or disjunctive
*
* @throws \InvalidArgumentException If less than 2 constraints are passed
*/
public
function
__construct
(
array
$constraints
,
$conjunctive
=
true
)
{
if
(
\count
(
$constraints
)
<
2
)
{
throw
new
\InvalidArgumentException
(
'Must provide at least two constraints for a MultiConstraint. Use '
.
'the regular Constraint class for one constraint only or MatchAllConstraint for none. You may use '
.
'MultiConstraint::create() which optimizes and handles those cases automatically.'
);
}
$this
->
constraints
=
$constraints
;
$this
->
conjunctive
=
$conjunctive
;
}
/**
* @return ConstraintInterface[]
*/
public
function
getConstraints
()
{
return
$this
->
constraints
;
}
/**
* @return bool
*/
public
function
isConjunctive
()
{
return
$this
->
conjunctive
;
}
/**
* @return bool
*/
public
function
isDisjunctive
()
{
return
!
$this
->
conjunctive
;
}
/**
* {@inheritDoc}
*/
public
function
compile
(
$otherOperator
)
{
$parts
=
array
();
foreach
(
$this
->
constraints
as
$constraint
)
{
$code
=
$constraint
->
compile
(
$otherOperator
);
if
(
$code
===
'true'
)
{
if
(!
$this
->
conjunctive
)
{
return
'true'
;
}
}
elseif
(
$code
===
'false'
)
{
if
(
$this
->
conjunctive
)
{
return
'false'
;
}
}
else
{
$parts
[]
=
'('
.
$code
.
')'
;
}
}
if
(!
$parts
)
{
return
$this
->
conjunctive
?
'true'
:
'false'
;
}
return
$this
->
conjunctive
?
implode
(
'&&'
,
$parts
)
:
implode
(
'||'
,
$parts
);
}
/**
* @param ConstraintInterface $provider
*
* @return bool
*/
public
function
matches
(
ConstraintInterface
$provider
)
{
if
(
false
===
$this
->
conjunctive
)
{
foreach
(
$this
->
constraints
as
$constraint
)
{
if
(
$provider
->
matches
(
$constraint
))
{
return
true
;
}
}
return
false
;
}
// when matching a conjunctive and a disjunctive multi constraint we have to iterate over the disjunctive one
// otherwise we'd return true if different parts of the disjunctive constraint match the conjunctive one
// which would lead to incorrect results, e.g. [>1 and <2] would match [<1 or >2] although they do not intersect
if
(
$provider
instanceof
MultiConstraint
&&
$provider
->
isDisjunctive
())
{
return
$provider
->
matches
(
$this
);
}
foreach
(
$this
->
constraints
as
$constraint
)
{
if
(!
$provider
->
matches
(
$constraint
))
{
return
false
;
}
}
return
true
;
}
/**
* {@inheritDoc}
*/
public
function
setPrettyString
(
$prettyString
)
{
$this
->
prettyString
=
$prettyString
;
}
/**
* {@inheritDoc}
*/
public
function
getPrettyString
()
{
if
(
$this
->
prettyString
)
{
return
$this
->
prettyString
;
}
return
(
string
)
$this
;
}
/**
* {@inheritDoc}
*/
public
function
__toString
()
{
if
(
$this
->
string
!==
null
)
{
return
$this
->
string
;
}
$constraints
=
array
();
foreach
(
$this
->
constraints
as
$constraint
)
{
$constraints
[]
=
(
string
)
$constraint
;
}
return
$this
->
string
=
'['
.
implode
(
$this
->
conjunctive
?
' '
:
' || '
,
$constraints
)
.
']'
;
}
/**
* {@inheritDoc}
*/
public
function
getLowerBound
()
{
$this
->
extractBounds
();
if
(
null
===
$this
->
lowerBound
)
{
throw
new
\LogicException
(
'extractBounds should have populated the lowerBound property'
);
}
return
$this
->
lowerBound
;
}
/**
* {@inheritDoc}
*/
public
function
getUpperBound
()
{
$this
->
extractBounds
();
if
(
null
===
$this
->
upperBound
)
{
throw
new
\LogicException
(
'extractBounds should have populated the upperBound property'
);
}
return
$this
->
upperBound
;
}
/**
* Tries to optimize the constraints as much as possible, meaning
* reducing/collapsing congruent constraints etc.
* Does not necessarily return a MultiConstraint instance if
* things can be reduced to a simple constraint
*
* @param ConstraintInterface[] $constraints A set of constraints
* @param bool $conjunctive Whether the constraints should be treated as conjunctive or disjunctive
*
* @return ConstraintInterface
*/
public
static
function
create
(
array
$constraints
,
$conjunctive
=
true
)
{
if
(
0
===
\count
(
$constraints
))
{
return
new
MatchAllConstraint
();
}
if
(
1
===
\count
(
$constraints
))
{
return
$constraints
[
0
];
}
$optimized
=
self
::
optimizeConstraints
(
$constraints
,
$conjunctive
);
if
(
$optimized
!==
null
)
{
list
(
$constraints
,
$conjunctive
)
=
$optimized
;
if
(
\count
(
$constraints
)
===
1
)
{
return
$constraints
[
0
];
}
}
return
new
self
(
$constraints
,
$conjunctive
);
}
/**
* @param ConstraintInterface[] $constraints
* @param bool $conjunctive
* @return ?array
*
* @phpstan-return array{0: list<ConstraintInterface>, 1: bool}|null
*/
private
static
function
optimizeConstraints
(
array
$constraints
,
$conjunctive
)
{
// parse the two OR groups and if they are contiguous we collapse
// them into one constraint
// [>= 1 < 2] || [>= 2 < 3] || [>= 3 < 4] => [>= 1 < 4]
if
(!
$conjunctive
)
{
$left
=
$constraints
[
0
];
$mergedConstraints
=
array
();
$optimized
=
false
;
for
(
$i
=
1
,
$l
=
\count
(
$constraints
);
$i
<
$l
;
$i
++)
{
$right
=
$constraints
[
$i
];
if
(
$left
instanceof
self
&&
$left
->
conjunctive
&&
$right
instanceof
self
&&
$right
->
conjunctive
&&
\count
(
$left
->
constraints
)
===
2
&&
\count
(
$right
->
constraints
)
===
2
&&
(
$left0
=
(
string
)
$left
->
constraints
[
0
])
&&
$left0
[
0
]
===
'>'
&&
$left0
[
1
]
===
'='
&&
(
$left1
=
(
string
)
$left
->
constraints
[
1
])
&&
$left1
[
0
]
===
'<'
&&
(
$right0
=
(
string
)
$right
->
constraints
[
0
])
&&
$right0
[
0
]
===
'>'
&&
$right0
[
1
]
===
'='
&&
(
$right1
=
(
string
)
$right
->
constraints
[
1
])
&&
$right1
[
0
]
===
'<'
&&
substr
(
$left1
,
2
)
===
substr
(
$right0
,
3
)
)
{
$optimized
=
true
;
$left
=
new
MultiConstraint
(
array
(
$left
->
constraints
[
0
],
$right
->
constraints
[
1
],
),
true
);
}
else
{
$mergedConstraints
[]
=
$left
;
$left
=
$right
;
}
}
if
(
$optimized
)
{
$mergedConstraints
[]
=
$left
;
return
array
(
$mergedConstraints
,
false
);
}
}
// TODO: Here's the place to put more optimizations
return
null
;
}
/**
* @return void
*/
private
function
extractBounds
()
{
if
(
null
!==
$this
->
lowerBound
)
{
return
;
}
foreach
(
$this
->
constraints
as
$constraint
)
{
if
(
null
===
$this
->
lowerBound
||
null
===
$this
->
upperBound
)
{
$this
->
lowerBound
=
$constraint
->
getLowerBound
();
$this
->
upperBound
=
$constraint
->
getUpperBound
();
continue
;
}
if
(
$constraint
->
getLowerBound
()->
compareTo
(
$this
->
lowerBound
,
$this
->
isConjunctive
()
?
'>'
:
'<'
))
{
$this
->
lowerBound
=
$constraint
->
getLowerBound
();
}
if
(
$constraint
->
getUpperBound
()->
compareTo
(
$this
->
upperBound
,
$this
->
isConjunctive
()
?
'<'
:
'>'
))
{
$this
->
upperBound
=
$constraint
->
getUpperBound
();
}
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, May 16, 21:25 (1 d, 13 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
3f/63/d9f38ed53c41b0f292e71824f794
Default Alt Text
MultiConstraint.php (9 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment