This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | #!/usr/bin/env php |
||
0 ignored issues
–
show
|
|||
2 | <?php |
||
3 | |||
4 | define('IGNORE_UI', true); |
||
5 | |||
6 | require_once __DIR__ . '/../common.inc.php'; |
||
7 | require_once __DIR__ . '/../constants.inc.php'; |
||
8 | |||
9 | use smtech\GradingAnalytics\Toolbox; |
||
10 | use smtech\CanvasPest\CanvasPest; |
||
11 | |||
12 | // http://stackoverflow.com/a/21896310 |
||
13 | function hoursRange($lower = 0, $upper = 86400, $step = 3600, $keyFormat = '', $value = '', $valueIsFormat = false) |
||
14 | { |
||
15 | $times = array(); |
||
16 | |||
17 | if (empty( $value ) && $valueIsFormat) { |
||
18 | $value = 'g:i a'; |
||
19 | } |
||
20 | |||
21 | if (empty($keyFormat)) { |
||
22 | $keyFormat = 'g:i a'; |
||
23 | } |
||
24 | |||
25 | foreach (range( $lower, $upper, $step ) as $increment) { |
||
26 | $increment = gmdate( $keyFormat, $increment ); |
||
27 | |||
28 | list( $hour, $minutes ) = explode( ':', $increment ); |
||
29 | |||
30 | $date = new DateTime( $hour . ':' . $minutes ); |
||
31 | |||
32 | $times[(string) $increment] = ($valueIsFormat ? $date->format( $value ) : $value); |
||
33 | } |
||
34 | |||
35 | return $times; |
||
36 | } |
||
37 | |||
38 | function collectStatistics($term, Toolbox $toolbox) |
||
0 ignored issues
–
show
collectStatistics uses the super-global variable $_SESSION which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
![]() |
|||
39 | { |
||
40 | $courses = $toolbox->api_get( |
||
41 | '/accounts/' . $toolbox->config(Toolbox::TOOL_CANVAS_ACCOUNT_ID) . '/courses', |
||
42 | array( |
||
43 | 'with_enrollments' => 'true', |
||
44 | 'enrollment_term_id' => $term |
||
45 | ) |
||
46 | ); |
||
47 | |||
48 | // so that everything has a consistent benchmark |
||
49 | $timestamp = time(); |
||
50 | |||
51 | foreach ($courses as $course) { |
||
0 ignored issues
–
show
The expression
$courses of type object<smtech\CanvasPest...CanvasPest\CanvasArray> is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
![]() |
|||
52 | $statistic = array( |
||
53 | 'timestamp' => date(DATE_ISO8601, $timestamp), |
||
54 | 'course[id]' => $course['id'], |
||
55 | 'course[name]' => $course['name'], |
||
56 | 'course[account_id]' => $course['account_id'], |
||
57 | 'gradebook_url' => $_SESSION[CANVAS_INSTANCE_URL] . "/courses/{$course['id']}/gradebook2", |
||
58 | 'assignments_due_count' => 0, |
||
59 | 'dateless_assignment_count' => 0, |
||
60 | 'created_after_due_count' => 0, |
||
61 | 'gradeable_assignment_count' => 0, |
||
62 | 'graded_assignment_count' => 0, |
||
63 | 'zero_point_assignment_count' => 0, |
||
64 | 'analytics_page' => $_SESSION[CANVAS_INSTANCE_URL] . "/courses/{$course['id']}/external_tools/" . $toolbox->config(Toolbox::TOOL_CANVAS_EXTERNAL_TOOL_ID) |
||
65 | ); |
||
66 | |||
67 | $teacherIds = array(); |
||
68 | $teacherNames = array(); |
||
69 | $teachers = $toolbox->api_get( |
||
70 | "/courses/{$course['id']}/enrollments", |
||
71 | array( |
||
72 | 'type[]' => 'TeacherEnrollment' |
||
73 | ) |
||
74 | ); |
||
75 | foreach ($teachers as $teacher) { |
||
0 ignored issues
–
show
The expression
$teachers of type object<smtech\CanvasPest...CanvasPest\CanvasArray> is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
![]() |
|||
76 | $teacherIds[] = $teacher['user']['id']; |
||
77 | $teacherNames[] = $teacher['user']['sortable_name']; |
||
78 | } |
||
79 | $statistic['teacher[id]s'] = serialize($teacherIds); |
||
80 | $statistic['teacher[sortable_name]s'] = serialize($teacherNames); |
||
81 | |||
82 | $account = $toolbox->api_get("/accounts/{$course['account_id']}"); |
||
83 | $statistic['account[name]'] = $account['name']; |
||
84 | |||
85 | // ignore classes with no teachers (how do they even exist? weird.) |
||
86 | if (count($teacherIds) != 0) { |
||
87 | $statistic['student_count'] = 0; |
||
88 | $students = $toolbox->api_get( |
||
89 | "/courses/{$course['id']}/enrollments", |
||
90 | array( |
||
91 | 'type[]' => 'StudentEnrollment' |
||
92 | ) |
||
93 | ); |
||
94 | $statistic['student_count'] = $students->count(); |
||
0 ignored issues
–
show
The method
count does only exist in smtech\CanvasPest\CanvasArray , but not in smtech\CanvasPest\CanvasObject .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
![]() |
|||
95 | |||
96 | // ignore classes with no students |
||
97 | if ($statistic['student_count'] != 0) { |
||
98 | $assignments = $toolbox->api_get( |
||
99 | "/courses/{$course['id']}/assignments" |
||
100 | ); |
||
101 | |||
102 | $gradedSubmissionsCount = 0; |
||
103 | $turnAroundTimeTally = 0; |
||
104 | $leadTimeTally = 0; |
||
105 | $createdModifiedHistogram = array( |
||
106 | HISTOGRAM_CREATED => hoursRange(0, 86400, 3600, '', 0), |
||
107 | HISTOGRAM_MODIFIED => hoursRange(0, 86400, 3600, '', 0) |
||
108 | ); |
||
109 | |||
110 | foreach ($assignments as $assignment) { |
||
0 ignored issues
–
show
The expression
$assignments of type object<smtech\CanvasPest...CanvasPest\CanvasArray> is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
![]() |
|||
111 | // ignore unpublished assignments |
||
112 | if ($assignment['published'] == true) { |
||
113 | // check for due dates |
||
114 | $dueDate = new DateTime($assignment['due_at']); |
||
115 | $dueDate->setTimeZone(new DateTimeZone(SCHOOL_TIME_ZONE)); |
||
116 | if (($timestamp - $dueDate->getTimestamp()) > 0) { |
||
117 | $statistic['assignments_due_count']++; |
||
118 | |||
119 | // update created_modified_histogram |
||
120 | $createdAt = new DateTime($assignment['created_at']); |
||
121 | $createdAt->setTimeZone(new DateTimeZone(SCHOOL_TIME_ZONE)); |
||
122 | $updatedAt = new DateTime($assignment['updated_at']); |
||
123 | $updatedAt->setTimeZone(new DateTimeZone(SCHOOL_TIME_ZONE)); |
||
124 | $createdModifiedHistogram[HISTOGRAM_CREATED][$createdAt->format('g:00 a')]++; |
||
125 | if ($createdAt != $updatedAt) { |
||
126 | $createdModifiedHistogram[HISTOGRAM_MODIFIED][$updatedAt->format('g:00 a')]++; |
||
127 | } |
||
128 | |||
129 | // tally lead time on the assignment |
||
130 | $leadTimeTally += strtotime($assignment['due_at']) - strtotime($assignment['created_at']); |
||
131 | |||
132 | // was the assignment created after it was due? |
||
133 | if (strtotime($assignment['due_at']) < strtotime($assignment['created_at'])) { |
||
134 | $statistic['created_after_due_count']++; |
||
135 | } |
||
136 | |||
137 | // ignore ungraded assignments |
||
138 | if ($assignment['grading_type'] != 'not_graded') { |
||
139 | $statistic['gradeable_assignment_count']++; |
||
140 | $hasBeenGraded = false; |
||
141 | |||
142 | // tally zero point assignments |
||
143 | if ($assignment['points_possible'] == '0') { |
||
144 | $statistic['zero_point_assignment_count']++; |
||
145 | } |
||
146 | |||
147 | // build submission statistic |
||
148 | $submissions = $toolbox->api_get( |
||
149 | "/courses/{$course['id']}/assignments/{$assignment['id']}/submissions" |
||
150 | ); |
||
151 | foreach ($submissions as $submission) { |
||
0 ignored issues
–
show
The expression
$submissions of type object<smtech\CanvasPest...CanvasPest\CanvasArray> is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
![]() |
|||
152 | if ($submission['workflow_state'] == 'graded') { |
||
153 | if ($hasBeenGraded == false) { |
||
0 ignored issues
–
show
|
|||
154 | $hasBeenGraded = true; |
||
155 | $statistic['graded_assignment_count']++; |
||
156 | } |
||
157 | $gradedSubmissionsCount++; |
||
158 | $turnAroundTimeTally += max( |
||
159 | 0, |
||
160 | strtotime($submission['graded_at']) - strtotime($assignment['due_at']) |
||
161 | ); |
||
162 | } |
||
163 | } |
||
164 | |||
165 | if (!$hasBeenGraded) { |
||
166 | if (array_key_exists('oldest_ungraded_assignment_due_date', $statistic)) { |
||
167 | if (strtotime($assignment['due_at']) < strtotime($statistic['oldest_ungraded_assignment_due_date'])) { |
||
168 | $statistic['oldest_ungraded_assignment_due_date'] = $assignment['due_at']; |
||
169 | $statistic['oldest_ungraded_assignment_url'] = $assignment['html_url']; |
||
170 | $statistic['oldest_ungraded_assignment_name'] = $assignment['name']; |
||
171 | } |
||
172 | } else { |
||
173 | $statistic['oldest_ungraded_assignment_due_date'] = $assignment['due_at']; |
||
174 | $statistic['oldest_ungraded_assignment_url'] = $assignment['html_url']; |
||
175 | $statistic['oldest_ungraded_assignment_name'] = $assignment['name']; |
||
176 | } |
||
177 | } |
||
178 | } |
||
179 | } else { |
||
180 | $statistic['dateless_assignment_count']++; |
||
181 | } |
||
182 | } |
||
183 | } |
||
184 | |||
185 | $statistic['created_modified_histogram'] = serialize($createdModifiedHistogram); |
||
186 | |||
187 | // calculate average submissions graded per assignment (if non-zero) |
||
188 | if ($statistic['gradeable_assignment_count'] && $statistic['student_count']) { |
||
189 | $statistic['average_submissions_graded'] = $gradedSubmissionsCount / ($statistic['gradeable_assignment_count'] * $statistic['student_count']); |
||
190 | } |
||
191 | |||
192 | // calculate the average lead-time on assignments |
||
193 | if ($statistic['assignments_due_count']) { |
||
194 | $statistic['average_assignment_lead_time'] = $leadTimeTally / $statistic['assignments_due_count'] / 60 / 60 / 24; |
||
195 | } |
||
196 | |||
197 | // calculate average grading turn-around per submission |
||
198 | if ($gradedSubmissionsCount) { |
||
199 | $statistic['average_grading_turn_around'] = $turnAroundTimeTally / $gradedSubmissionsCount / 60 / 60 / 24; |
||
200 | } |
||
201 | |||
202 | $query = "INSERT INTO `course_statistics`"; |
||
203 | $fields = array(); |
||
204 | $values = array(); |
||
205 | while (list($field, $value) = each($statistic)) { |
||
206 | $fields[] = $field; |
||
207 | $values[] = $value; |
||
208 | } |
||
209 | $query .= ' (`' . implode('`, `', $fields) . "`) VALUES ('" . implode("', '", $values) . "')"; |
||
210 | $result = $toolbox->mysql_query($query); |
||
0 ignored issues
–
show
$result is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
211 | } |
||
212 | } |
||
213 | } |
||
214 | } |
||
215 | |||
216 | /* force API configuration from config file */ |
||
217 | $toolbox->setApi(new CanvasPest( |
||
218 | $_SESSION[CANVAS_INSTANCE_URL] . '/api/v1', |
||
219 | $toolbox->config(Toolbox::TOOL_CANVAS_API)['token'] |
||
220 | )); |
||
221 | |||
222 | /* collect data on terms currently in session */ |
||
223 | try { |
||
224 | $terms = $toolbox->api_get('accounts/1/terms'); |
||
225 | $now = strtotime('now'); |
||
226 | foreach ($terms['enrollment_terms'] as $term) { |
||
227 | if (isset($term['start_at']) && isset($term['end_at'])) { |
||
228 | if ((strtotime($term['start_at']) <= $now) && ($now <= strtotime($term['end_at']))) { |
||
229 | collectStatistics($term['id'], $toolbox); |
||
230 | } |
||
231 | } |
||
232 | } |
||
233 | } catch (Exception $e) { |
||
234 | echo $e->getMessage(); |
||
235 | } |
||
236 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.