Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1f31d46
Docker for testing
jasonmatos23-git Oct 1, 2021
34d7ece
Add broken and redirected link rule
jasonmatos23-git Oct 4, 2021
a52ee2b
Add broken and redirected link test
jasonmatos23-git Oct 4, 2021
4986b9a
Link checker baseline
jasonmatos23-git Oct 6, 2021
63875e1
Include metadata of redirected url for BrokenRedirectedLink rule
jasonmatos23-git Oct 8, 2021
bfe3198
Add redirected tests to BrokenRedirectedLinkTest
jasonmatos23-git Oct 8, 2021
c72b1ff
Add regex to BrokenRedirectedLink rule
jasonmatos23-git Oct 8, 2021
d70cfd5
Testing broken and redirected link rule
jasonmatos23-git Oct 8, 2021
ad10890
Add BrokenRedirectedLink to rules json
jasonmatos23-git Oct 8, 2021
c3c67de
Merge pull request #2 from jasonmatos23-git/dev
jasonmatos23-git Oct 8, 2021
ce5a343
Add test checking that a redirect link is contained in metadata
jasonmatos23-git Oct 11, 2021
8be23dd
Merge branch 'cidilabs:master' into master
jasonmatos23-git Oct 13, 2021
0c81454
Merge pull request #3 from jasonmatos23-git/dev
jasonmatos23-git Oct 13, 2021
9053e53
Clean up docker testing components and comments
jasonmatos23-git Oct 13, 2021
0bf8443
Merge pull request #4 from jasonmatos23-git/dev
jasonmatos23-git Oct 13, 2021
cd87784
Split broken and redirected link tests
AlanFCMV Oct 19, 2021
21a018c
Add new split rules
AlanFCMV Oct 20, 2021
2a4f1c4
update curl option
AlanFCMV Oct 21, 2021
1e85f34
Fix comparisons
AlanFCMV Oct 21, 2021
59b142e
Use parse_url
AlanFCMV Oct 21, 2021
390712b
Refactor to check for permanent redirects only
AlanFCMV Oct 21, 2021
65c312e
Add back parse_url
AlanFCMV Oct 21, 2021
efc9cf4
Fix parse_url
AlanFCMV Oct 21, 2021
379173d
Single step link checking
AlanFCMV Oct 22, 2021
4c24454
Fix broken links also showing as redirected
AlanFCMV Oct 22, 2021
b706dce
Fix redirectedlink rule curl calls
AlanFCMV Oct 22, 2021
1bb0e3a
Fix wrong link getting passed
AlanFCMV Oct 25, 2021
47e2e69
Fix wrong redirected link returned
AlanFCMV Oct 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/Rule/BrokenLink.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace CidiLabs\PhpAlly\Rule;

use DOMElement;

/**
* Links that are broken need to be removed or manually updated.
* Based on UDOIT 2.8.X https://github.com/ucfopen/UDOIT/blob/classic/lib/Udoit.php
* contributions by Emily Sachs
*/
class BrokenLink extends BaseRule
{

public function id()
{
return self::class;
}

public function check()
{
$links = array();
foreach ($this->getAllElements('a') as $a) {
$href = $a->getAttribute('href');
if ($href) {
$links[$href] = $a;
}
}
$this->checkLink($links);

return count($this->issues);
}

private function checkLink($links) {
$curls = array();
$mcurl = curl_multi_init();
foreach (array_keys($links) as $i => $link) {
$curls[$i] = curl_init();
curl_setopt($curls[$i], CURLOPT_URL, $link);
curl_setopt($curls[$i], CURLOPT_HEADER, true);
curl_setopt($curls[$i], CURLOPT_NOBODY, true);
curl_setopt($curls[$i], CURLOPT_REFERER, true);
curl_setopt($curls[$i], CURLOPT_TIMEOUT, 2);
curl_setopt($curls[$i], CURLOPT_TIMEOUT, 2);
curl_setopt($curls[$i], CURLOPT_AUTOREFERER, true);
curl_setopt($curls[$i], CURLOPT_RETURNTRANSFER, true);
curl_setopt($curls[$i], CURLOPT_FOLLOWLOCATION, true);
curl_multi_add_handle($mcurl, $curls[$i]);
}
$running = null;
do {
curl_multi_exec($mcurl, $running);
} while ($running > 0);
foreach (array_keys($links) as $i => $link) {
$status = curl_getinfo($curls[$i], CURLINFO_RESPONSE_CODE);
// If the status is greater than or equal to 400 the link is broken.
if ($status >= 400) {
$this->setIssue($links[$link]);
}
curl_multi_remove_handle($mcurl, $curls[$i]);
}
curl_multi_close($mcurl);
}
}
114 changes: 114 additions & 0 deletions src/Rule/RedirectedLink.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

namespace CidiLabs\PhpAlly\Rule;

use DOMElement;

/**
* Links that are permanently redirected should be updated with the new link.
* Based on UDOIT 2.8.X https://github.com/ucfopen/UDOIT/blob/classic/lib/Udoit.php
* contributions by Emily Sachs
*/
class RedirectedLink extends BaseRule
{

public function id()
{
return self::class;
}

public function check()
{
$links = array();
foreach ($this->getAllElements('a') as $a) {
$href = $a->getAttribute('href');
if ($href) {
$links[$href] = $a;
}
}
$this->checkLink($links);

return count($this->issues);
}

private function checkLink($links) {
$curls = array();
$mcurl = curl_multi_init();
foreach (array_keys($links) as $i => $link) {
$curls[$i] = curl_init();
curl_setopt($curls[$i], CURLOPT_URL, $link);
curl_setopt($curls[$i], CURLOPT_HEADER, true);
curl_setopt($curls[$i], CURLOPT_NOBODY, true);
curl_setopt($curls[$i], CURLOPT_REFERER, true);
curl_setopt($curls[$i], CURLOPT_TIMEOUT, 2);
curl_setopt($curls[$i], CURLOPT_TIMEOUT, 2);
curl_setopt($curls[$i], CURLOPT_AUTOREFERER, true);
curl_setopt($curls[$i], CURLOPT_RETURNTRANSFER, true);
curl_setopt($curls[$i], CURLOPT_FOLLOWLOCATION, true);
curl_multi_add_handle($mcurl, $curls[$i]);
}
$running = null;
do {
curl_multi_exec($mcurl, $running);
} while ($running > 0);
foreach (array_keys($links) as $i => $link) {
$status = curl_getinfo($curls[$i], CURLINFO_RESPONSE_CODE);
// If the status is 400 or greater the link is broken so dont bother checking.
if ($status < 400) {
$this->checkRedirect($links[$link]);
}
curl_multi_remove_handle($mcurl, $curls[$i]);
}
curl_multi_close($mcurl);
}

private function checkRedirect($original) {
$link = $original->getAttribute('href');
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $link);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_NOBODY, true);
curl_setopt($curl, CURLOPT_REFERER, true);
curl_setopt($curl, CURLOPT_TIMEOUT, 2);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

curl_exec($curl);
$redirect = curl_getinfo($curl, CURLINFO_REDIRECT_URL);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
curl_close($curl);

// Only permanent redirections are a problem
if ($status === 301 || $status === 308) {
$this->followPermanentRedirects($original, $redirect);
}
}

private function followPermanentRedirects($original, $link, $maxRedirects = 20) {
// Avoid infinite calls. 20 is chrome and firefox redirect limit.
if ($maxRedirects < 1) {
$this->setIssue($original, null, json_encode(array('redirect_url' => $link)));
return;
}

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $link);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_NOBODY, true);
curl_setopt($curl, CURLOPT_REFERER, true);
curl_setopt($curl, CURLOPT_TIMEOUT, 2);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

curl_exec($curl);
$redirect = curl_getinfo($curl, CURLINFO_REDIRECT_URL);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
curl_close($curl);

// Continue until we run out of permanent redirects
if ($status === 301 || $status === 308) {
$this->followPermanentRedirects($original, $redirect, $maxRedirects - 1);
} else {
$this->setIssue($original, null, json_encode(array('redirect_url' => $link)));
}
}
}

2 changes: 2 additions & 0 deletions src/rules.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"CidiLabs\\PhpAlly\\Rule\\AnchorSuspiciousLinkText",
"CidiLabs\\PhpAlly\\Rule\\BaseFontIsNotUsed",
"CidiLabs\\PhpAlly\\Rule\\BlinkIsNotUsed",
"CidiLabs\\PhpAlly\\Rule\\BrokenLink",
"CidiLabs\\PhpAlly\\Rule\\ContentTooLong",
"CidiLabs\\PhpAlly\\Rule\\CssTextHasContrast",
"CidiLabs\\PhpAlly\\Rule\\CssTextStyleEmphasize",
Expand All @@ -29,6 +30,7 @@
"CidiLabs\\PhpAlly\\Rule\\ObjectTagDetected",
"CidiLabs\\PhpAlly\\Rule\\ParagraphNotUsedAsHeader",
"CidiLabs\\PhpAlly\\Rule\\PreShouldNotBeUsedForTabularValues",
"CidiLabs\\PhpAlly\\Rule\\RedirectedLink",
"CidiLabs\\PhpAlly\\Rule\\TableDataShouldHaveTableHeader",
"CidiLabs\\PhpAlly\\Rule\\TableHeaderShouldHaveScope",
"CidiLabs\\PhpAlly\\Rule\\VideoCaptionsMatchCourseLanguage",
Expand Down
35 changes: 35 additions & 0 deletions tests/BrokenLinkTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

use CidiLabs\PhpAlly\Rule\BrokenLink;

class BrokenLinkTest extends PhpAllyTestCase {
public function testCheckValid()
{
$html = '<div><a href="www.google.com">I am a link.</a><div>';
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->loadHTML($html);
$rule = new BrokenLink($dom);

$this->assertEquals(0, $rule->check(), 'BrokenLink should have no issue.');
}

public function testCheckBroken400()
{
$html = '<div><a href="http://www.deadlinkcity.com/error-page.asp?e=400">I am a link.</a><div>';
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->loadHTML($html);
$rule = new BrokenLink($dom);

$this->assertEquals(1, $rule->check(), 'BrokenLink should have one issue.');
}

public function testCheckBroken404()
{
$html = '<div><a href="https://webaim.org/brokenlink">I am a link.</a><div>';
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->loadHTML($html);
$rule = new BrokenLink($dom);

$this->assertEquals(1, $rule->check(), 'BrokenLink should have one issue.');
}
}
12 changes: 6 additions & 6 deletions tests/PhpAllyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function testCheckOne()
$this->phpAllyReportTest($report);
}

public function testCheckMany()
public function testCheckMany()
{
$ally = new PhpAlly();
$options = [
Expand All @@ -41,17 +41,17 @@ public function testCheckMany()
$report = $ally->checkMany($this->getManyHtml(), $ally->getRuleIds(), $options);
$issues = $report->getIssues();
$issue = reset($issues);
$this->assertCount(6, $issues, 'Total report should have 5 issues.');

$this->assertCount(6, $issues, 'Total report should have 6 issues.');
$this->phpAllyIssueTest($issue);
$this->phpAllyReportTest($report);
}


protected function phpAllyReportTest($report)
{
$issues = $report->getIssues();

foreach($issues as $issue) {
$this->phpAllyIssueTest($issue);
}
Expand All @@ -64,4 +64,4 @@ protected function phpAllyIssueTest(PhpAllyIssue $issue)
$this->assertEquals(DOMElement::class, get_class($issue->getPreviewElement()), 'Issue return DomElement for getPreviewElement()');
}

}
}
10 changes: 5 additions & 5 deletions tests/PhpAllyTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ protected function getManyHtml()
{
return '<div>
<p style="color: #000;">Paragraph text is here.</p>
<a href="https://google.com"> </a>
<a href="https://google.com">Click Here</a>
<a href="https://google.com"> </a>
<a href="https://www.google.com/"> </a>
<a href="https://www.google.com/">Click Here</a>
<a href="https://www.google.com/"> </a>

<p style="color: #0000FF";"><strong>Paragraph text does have enough contrast.</strong></p>

<p>Paragraph text <span style="color: #000">has</span> enough contrast.</p>
Expand Down Expand Up @@ -129,4 +129,4 @@ protected function getImageHtml()
{
return '';
}
}
}
42 changes: 42 additions & 0 deletions tests/RedirectedLinkTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

use CidiLabs\PhpAlly\Rule\RedirectedLink;

class RedirectedLinkTest extends PhpAllyTestCase {
public function testCheckNotRedirected()
{
$html = '<div><a href="www.google.com">I am a link.</a><div>';
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->loadHTML($html);
$rule = new RedirectedLink($dom);

$this->assertEquals(0, $rule->check(), 'RedirectedLink should have no issue.');
}

public function testCheckRedirected()
{
$html = '<div><a href="https://online.ucf.edu/udoit">I am a link.</a><div>';
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->loadHTML($html);
$rule = new RedirectedLink($dom);

$this->assertEquals(1, $rule->check(), 'RedirectedLink should have one issue.');
}

public function testCheckRedirectedAndMetadata()
{
$html = '<div><a href="https://online.ucf.edu/udoit">I am a link.</a><div>';
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->loadHTML($html);
$rule = new RedirectedLink($dom);

// Check if metadata is present with a new link
$result = $rule->check();
if ($rule->getIssues() && count($rule->getIssues()) == 1) {
$meta = $rule->getIssues()[0]->getMetadata();
$result = 1 + $result;
}

$this->assertEquals(2, $result, 'RedirectedLink should have one issue.');
}
}