Skip to content

Commit

Permalink
Merge pull request #30 from Roave/feature/#5-detected-property-visibi…
Browse files Browse the repository at this point in the history
…lity-change

Feature/#5 detected property visibility change
  • Loading branch information
Ocramius authored Apr 15, 2018
2 parents 47534f7 + 358eca4 commit 7ceab28
Show file tree
Hide file tree
Showing 15 changed files with 635 additions and 18 deletions.
46 changes: 46 additions & 0 deletions src/Comparator/BackwardsCompatibility/ClassBased/MethodRemoved.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace Roave\ApiCompare\Comparator\BackwardsCompatibility\ClassBased;

use Assert\Assert;
use Roave\ApiCompare\Change;
use Roave\ApiCompare\Changes;
use Roave\BetterReflection\Reflection\ReflectionClass;
use Roave\BetterReflection\Reflection\ReflectionMethod;

final class MethodRemoved implements ClassBased
{
public function compare(ReflectionClass $fromClass, ReflectionClass $toClass) : Changes
{
Assert::that($fromClass->getName())->same($toClass->getName());

$removedMethods = array_diff_key(
array_change_key_case($this->accessibleMethods($fromClass), \CASE_UPPER),
array_change_key_case($this->accessibleMethods($toClass), \CASE_UPPER)
);

return Changes::fromArray(array_values(array_map(function (ReflectionMethod $method) use ($fromClass) : Change {
return Change::removed(
sprintf('Method %s#%s() was removed', $fromClass->getName(), $method->getName()),
true
);
}, $removedMethods)));
}

/** @return ReflectionMethod[] */
private function accessibleMethods(ReflectionClass $class) : array
{
$methods = array_filter($class->getMethods(), function (ReflectionMethod $method) : bool {
return $method->isPublic() || $method->isProtected();
});

return array_combine(
array_map(function (ReflectionMethod $method) : string {
return $method->getName();
}, $methods),
$methods
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

namespace Roave\ApiCompare\Comparator\BackwardsCompatibility\ClassBased;

use Assert\Assert;
use Roave\ApiCompare\Change;
use Roave\ApiCompare\Changes;
use Roave\BetterReflection\Reflection\ReflectionClass;
use Roave\BetterReflection\Reflection\ReflectionMethod;

final class MethodVisibilityReduced implements ClassBased
{
private const VISIBILITY_PRIVATE = 'private';

private const VISIBILITY_PROTECTED = 'protected';

private const VISIBILITY_PUBLIC = 'public';

public function compare(ReflectionClass $fromClass, ReflectionClass $toClass) : Changes
{
Assert::that($fromClass->getName())->same($toClass->getName());

$visibilitiesFrom = $this->methodVisibilities($fromClass);
$visibilitiesTo = $this->methodVisibilities($toClass);

$affectedVisibilities = array_filter(
array_combine(
array_keys(array_intersect_key($visibilitiesFrom, $visibilitiesTo)),
array_map(
function (string $visibilityFrom, string $visibilityTo) : array {
return [$visibilityFrom, $visibilityTo];
},
array_intersect_key($visibilitiesFrom, $visibilitiesTo),
array_intersect_key($visibilitiesTo, $visibilitiesFrom)
)
),
function (array $visibilities) : bool {
// Note: works because public, protected and private are (luckily) sortable
return $visibilities[0] > $visibilities[1];
}
);

return Changes::fromArray(array_values(array_map(function (string $methodName, array $visibilities) use (
$fromClass
) : Change {
return Change::changed(
sprintf(
'Method %s#%s() changed visibility from %s to %s',
$fromClass->getName(),
$fromClass->getMethod($methodName)->getName(),
$visibilities[0],
$visibilities[1]
),
true
);
}, array_keys($affectedVisibilities), $affectedVisibilities)));
}

/** @return string[] */
private function methodVisibilities(ReflectionClass $class) : array
{
$methods = $class->getMethods();

return array_combine(
array_map(function (ReflectionMethod $method) : string {
return $method->getName();
}, $methods),
array_map(function (ReflectionMethod $method) : string {
if ($method->isPublic()) {
return self::VISIBILITY_PUBLIC;
}

if ($method->isProtected()) {
return self::VISIBILITY_PROTECTED;
}

return self::VISIBILITY_PRIVATE;
}, $methods)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Roave\ApiCompare\Comparator\BackwardsCompatibility\ClassBased;

use Roave\ApiCompare\Changes;
use Roave\BetterReflection\Reflection\ReflectionClass;

final class MultiClassBased implements ClassBased
{
/** @var ClassBased[] */
private $checks;

public function __construct(ClassBased ...$checks)
{
$this->checks = $checks;
}

public function compare(ReflectionClass $fromClass, ReflectionClass $toClass) : Changes
{
return array_reduce(
$this->checks,
function (Changes $changes, ClassBased $check) use ($fromClass, $toClass) : Changes {
return $changes->mergeWith($check->compare($fromClass, $toClass));
},
Changes::new()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

namespace Roave\ApiCompare\Comparator\BackwardsCompatibility\ClassBased;

use Assert\Assert;
use Roave\ApiCompare\Change;
use Roave\ApiCompare\Changes;
use Roave\BetterReflection\Reflection\ReflectionClass;
use Roave\BetterReflection\Reflection\ReflectionProperty;

final class PropertyVisibilityReduced implements ClassBased
{
private const VISIBILITY_PRIVATE = 'private';

private const VISIBILITY_PROTECTED = 'protected';

private const VISIBILITY_PUBLIC = 'public';

public function compare(ReflectionClass $fromClass, ReflectionClass $toClass) : Changes
{
Assert::that($fromClass->getName())->same($toClass->getName());

$visibilitiesFrom = $this->propertyVisibilities($fromClass);
$visibilitiesTo = $this->propertyVisibilities($toClass);

$affectedVisibilities = array_filter(
array_combine(
array_keys(array_intersect_key($visibilitiesFrom, $visibilitiesTo)),
array_map(
function (string $visibilityFrom, string $visibilityTo) : array {
return [$visibilityFrom, $visibilityTo];
},
array_intersect_key($visibilitiesFrom, $visibilitiesTo),
array_intersect_key($visibilitiesTo, $visibilitiesFrom)
)
),
function (array $visibilities) : bool {
// Note: works because public, protected and private are (luckily) sortable
return $visibilities[0] > $visibilities[1];
}
);

return Changes::fromArray(array_values(array_map(function (string $propertyName, array $visibilities) use (
$fromClass
) : Change {
return Change::changed(
sprintf(
'Property %s#%s changed visibility from %s to %s',
$fromClass->getName(),
$propertyName,
$visibilities[0],
$visibilities[1]
),
true
);
}, array_keys($affectedVisibilities), $affectedVisibilities)));
}

/** @return string[] */
private function propertyVisibilities(ReflectionClass $class) : array
{
return array_map(function (ReflectionProperty $property) : string {
if ($property->isPublic()) {
return self::VISIBILITY_PUBLIC;
}

if ($property->isProtected()) {
return self::VISIBILITY_PROTECTED;
}

return self::VISIBILITY_PRIVATE;
}, $class->getProperties());
}
}
35 changes: 35 additions & 0 deletions test/asset/api/new/ClassWithMethodVisibilitiesBeingChanged.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);

namespace RoaveTestAsset;

class ClassWithMethodVisibilitiesBeingChanged
{
public function publicMaintainedPublic()
{
}
protected function publicReducedToProtected()
{
}
private function publicReducedToPrivate()
{
}
protected function protectedMaintainedProtected()
{
}
private function protectedReducedToPrivate()
{
}
public function protectedIncreasedToPublic()
{
}
private function privateMaintainedPrivate()
{
}
protected function privateIncreasedToProtected()
{
}
public function privateIncreasedToPublic()
{
}
}
20 changes: 20 additions & 0 deletions test/asset/api/new/ClassWithMethodsBeingRemoved.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);

namespace RoaveTestAsset;

class ClassWithMethodsBeingRemoved
{
public function nameCaseChangePublicMethod() {
}
public function keptPublicMethod() {
}
protected function nameCaseChangeProtectedMethod() {
}
protected function keptProtectedMethod() {
}
private function nameCaseChangePrivateMethod() {
}
private function keptPrivateMethod() {
}
}
17 changes: 17 additions & 0 deletions test/asset/api/new/ClassWithPropertyVisibilitiesBeingChanged.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);

namespace RoaveTestAsset;

class ClassWithPropertyVisibilitiesBeingChanged
{
public $publicMaintainedPublic;
protected $publicReducedToProtected;
private $publicReducedToPrivate;
protected $protectedMaintainedProtected;
private $protectedReducedToPrivate;
public $protectedIncreasedToPublic;
private $privateMaintainedPrivate;
protected $privateIncreasedToProtected;
public $privateIncreasedToPublic;
}
35 changes: 35 additions & 0 deletions test/asset/api/old/ClassWithMethodVisibilitiesBeingChanged.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);

namespace RoaveTestAsset;

class ClassWithMethodVisibilitiesBeingChanged
{
public function publicMaintainedPublic()
{
}
public function publicReducedToProtected()
{
}
public function publicReducedToPrivate()
{
}
protected function protectedMaintainedProtected()
{
}
protected function protectedReducedToPrivate()
{
}
protected function protectedIncreasedToPublic()
{
}
private function privateMaintainedPrivate()
{
}
private function privateIncreasedToProtected()
{
}
private function privateIncreasedToPublic()
{
}
}
26 changes: 26 additions & 0 deletions test/asset/api/old/ClassWithMethodsBeingRemoved.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);

namespace RoaveTestAsset;

class ClassWithMethodsBeingRemoved
{
public function removedPublicMethod() {
}
public function nameCaseChangePublicMethod() {
}
public function keptPublicMethod() {
}
protected function removedProtectedMethod() {
}
protected function nameCaseChangeProtectedMethod() {
}
protected function keptProtectedMethod() {
}
private function removedPrivateMethod() {
}
private function nameCaseChangePrivateMethod() {
}
private function keptPrivateMethod() {
}
}
17 changes: 17 additions & 0 deletions test/asset/api/old/ClassWithPropertyVisibilitiesBeingChanged.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);

namespace RoaveTestAsset;

class ClassWithPropertyVisibilitiesBeingChanged
{
public $publicMaintainedPublic;
public $publicReducedToProtected;
public $publicReducedToPrivate;
protected $protectedMaintainedProtected;
protected $protectedReducedToPrivate;
protected $protectedIncreasedToPublic;
private $privateMaintainedPrivate;
private $privateIncreasedToProtected;
private $privateIncreasedToPublic;
}
Loading

0 comments on commit 7ceab28

Please sign in to comment.