PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.5
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.5
1.9.5 1.9.4 1.9.3 trunk 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.6 1.6.1 1.6.10 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 All 40 releases
code-profiler / lib / class-table-profiles.php

class-table-profiles.php in Code Profiler – WordPress Performance Profiling and Debugging Made Easy 1.5, at lib/class-table-profiles.php

347 lines 9.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 +=====================================================================+
4 | ____ _ ____ __ _ _ |
5 | / ___|___ __| | ___ | _ \ _ __ ___ / _(_) | ___ _ __ |
6 | | | / _ \ / _` |/ _ \ | |_) | '__/ _ \| |_| | |/ _ \ '__| |
7 | | |__| (_) | (_| | __/ | __/| | | (_) | _| | | __/ | |
8 | \____\___/ \__,_|\___| |_| |_| \___/|_| |_|_|\___|_| |
9 | |
10 | (c) Jerome Bruandet ~ https://code-profiler.com/ |
11 +=====================================================================+
12 */
13
14 if (! defined( 'ABSPATH' ) ) { die( 'Forbidden' ); }
15
16 // =====================================================================
17
18 class CodeProfiler_Table_Profiles extends WP_List_Table {
19
20
21 private $default_files = [
22 'diskio',
23 'iostats',
24 'slugs',
25 'summary',
26 'composer'
27 ];
28 private $abspath;
29
30 function __construct() {
31
32 $this->abspath = rtrim( ABSPATH, '/\\' );
33
34 parent::__construct( array(
35 'singular' => esc_html__( 'profile', 'code-profiler' ),
36 'plural' => esc_html__( 'profiles', 'code-profiler' ),
37 'ajax' => false
38 ));
39 }
40
41 /*
42 * Empty list
43 */
44 function no_items() {
45 esc_html_e( 'No profile found.', 'code-profiler' );
46 }
47
48 /*
49 * Default
50 */
51 function column_default( $item, $column_name ) {
52
53 if ( $item[ $column_name ] == '-' ) {
54 // Old profiles (CP <=1.2) must not call number_format()
55 return $item[ $column_name ];
56 }
57 switch( $column_name ) {
58 case 'profile':
59 return $item[ $column_name ];
60 break;
61 case 'date':
62 return date('Y/m/d \@ H:i', $item[ $column_name ] );
63 break;
64 case 'time':
65 return $item[ $column_name ] .' s';
66 break;
67 case 'mem':
68 return number_format( $item[ $column_name ], 2 ) .' MB';
69 break;
70 case 'io':
71 case 'queries':
72 case 'items':
73 return number_format( $item[ $column_name ] );
74 break;
75 default:
76 return '';
77 }
78 }
79
80 /*
81 * Sortable columns
82 */
83 function get_sortable_columns() {
84 return array(
85 'profile' => array( 'profile', true ),
86 'date' => array( 'date', true ),
87 'items' => array( 'items', true ),
88 'time' => array( 'time', true ),
89 'mem' => array( 'mem', true ),
90 'io' => array( 'io', true ),
91 'queries' => array( 'queries', true )
92 );
93 }
94
95 /*
96 * Columns
97 */
98 function get_columns(){
99 return array(
100 'cb' => '<input type="checkbox" />',
101 'profile' => esc_html__( 'Profile', 'code-profiler' ),
102 'date' => esc_html__( 'Date', 'code-profiler' ),
103 'items' => esc_html__( 'Items', 'code-profiler' ),
104 'time' => esc_html__( 'Time', 'code-profiler' ),
105 'mem' => esc_html__( 'Memory', 'code-profiler' ),
106 'io' => esc_html__( 'File I/O', 'code-profiler' ),
107 'queries' => esc_html__( 'SQL', 'code-profiler' )
108 );
109 }
110
111 /*
112 * Sorting
113 */
114 function usort_reorder( $a, $b ) {
115 // Sort by date by default
116 $orderby = (! empty( $_GET['orderby'] ) ) ? sanitize_key( $_GET['orderby'] ) : 'ID';
117 $order = (! empty( $_GET['order'] ) ) ? sanitize_key( $_GET['order'] ) : 'asc';
118 $result = $this->cmp_num_or_string( $a[$orderby], $b[$orderby] );
119 return ( $order === 'asc' ) ? $result : -$result;
120 }
121
122 /*
123 * Sort string and numeric values differently
124 */
125 function cmp_num_or_string( $a, $b ) {
126 if ( is_numeric( $a ) && is_numeric( $b ) ) {
127 return ($a-$b) ? ($a-$b)/abs($a-$b) : 0;
128 } else {
129 return strcmp( $a, $b );
130 }
131 }
132
133
134 /*
135 * Row action links
136 */
137 function column_profile( $item ) {
138
139 // Keep sorting order for the "delete" action link
140 $orderby = (! empty( $_GET['orderby'] ) ) ? sanitize_key( $_GET['orderby'] ) : 'ID';
141 $order = (! empty( $_GET['order'] ) ) ? sanitize_key( $_GET['order'] ) : 'asc';
142
143 $actions = array(
144 'view' => sprintf(
145 '<a href="?page=code-profiler&cptab=profiles_list&action=%s&id=%s&section=1">%s</a>',
146 'view_profile', esc_attr( $item['ID'] ), esc_attr__('View')
147 ),
148 'delete' => sprintf(
149 '<a href="?page=code-profiler&cptab=profiles_list&action=%s&profiles[]=%s&_wpnonce=%s&orderby=%s&order=%s" '.
150 'onclick="return cpjs_delete_profile();">%s</a>',
151 'delete_profiles', esc_attr( $item['ID'] ),
152 wp_create_nonce( 'bulk-'. $this->_args['plural'] ), $orderby, $order, esc_attr__('Delete')
153 )
154 );
155 return sprintf( '%1$s %2$s', $item['profile'], $this->row_actions( $actions ) );
156 }
157
158 /*
159 * Bulk action menu (delete)
160 */
161 function get_bulk_actions() {
162 return array(
163 'delete_profiles' => esc_html__('Delete')
164 );
165 }
166
167 /*
168 * Checkboxes
169 */
170 function column_cb($item) {
171 return sprintf(
172 '<input type="checkbox" name="profiles[]" value="%s" />',
173 esc_attr( $item['ID'] )
174 );
175 }
176
177 /*
178 * Prepare to display profiles
179 */
180 function prepare_items() {
181 $columns = $this->get_columns();
182 $hidden = array();
183 $sortable = $this->get_sortable_columns();
184 $this->_column_headers = array( $columns, $hidden, $sortable );
185
186 // Fetch our data
187 $profile = $this->fetch_profiles();
188 usort( $profile, array( &$this, 'usort_reorder' ) );
189
190 $per_page = 30;
191
192 // If we just delete some profiles, we go back to page #1:
193 if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'delete_profiles' ) {
194 $current_page = 1;
195 } else {
196 $current_page = $this->get_pagenum();
197 }
198
199 $total_items = count( $profile );
200 $this->items = array_slice( $profile,( ( $current_page-1 )* $per_page ), $per_page );
201
202 $this->set_pagination_args( array(
203 'total_items' => $total_items,
204 'per_page' => $per_page
205 ));
206 }
207
208 /*
209 * Retrieve all profiles
210 */
211 function fetch_profiles() {
212
213 $profiles = [];
214 $glob = glob( CODE_PROFILER_UPLOAD_DIR .'/*.slugs.profile' );
215
216 if ( is_array( $glob ) ) {
217 $count = 0;
218 foreach( $glob as $path ) {
219 $file = basename( $path );
220 if ( preg_match( '`^(\d{10}\.\d+)\.(.+?)\.(?:slugs)\.profile$`', $file, $match ) ) {
221
222 $error = 0; $fsize = 0;
223 // Make sure we have all profile files
224 foreach( $this->default_files as $pname ) {
225 // Ignore these ones, there aren't mandatory
226 if ( in_array( $pname, [ 'composer', 'summary' ] ) ) { continue; }
227 if ( file_exists( CODE_PROFILER_UPLOAD_DIR ."/{$match[1]}.{$match[2]}.$pname.profile" ) ) {
228 $fsize += filesize( CODE_PROFILER_UPLOAD_DIR ."/{$match[1]}.{$match[2]}.$pname.profile" );
229 } else {
230 $error = 1;
231 break;
232 }
233 }
234 // Delete if incomplete
235 if ( $error ) {
236 foreach( $this->default_files as $pname ) {
237 if ( file_exists( CODE_PROFILER_UPLOAD_DIR ."/{$match[1]}.{$match[2]}.$pname.profile" ) ) {
238 unlink( CODE_PROFILER_UPLOAD_DIR ."/{$match[1]}.{$match[2]}.$pname.profile" );
239 }
240 }
241 continue;
242 }
243
244 // Search query
245 $search = false;
246 if (! empty( $_REQUEST['s'] ) ) {
247 foreach( $this->default_files as $pname ) {
248 // Ignore these ones, there aren't mandatory
249 if ( in_array( $pname, [ 'composer', 'summary' ] ) ) { continue; }
250 $search = $this->search_profile_file(
251 CODE_PROFILER_UPLOAD_DIR ."/{$match[1]}.{$match[2]}.$pname.profile",
252 sanitize_text_field( $_REQUEST['s'] )
253 );
254 if ( $search === true ) {
255 break;
256 }
257 }
258 }
259 if (! empty( $_REQUEST['s'] ) && $search === false ) {
260 continue;
261 }
262
263 // Fetch the summary file (CP >1.2)
264 if ( file_exists( CODE_PROFILER_UPLOAD_DIR ."/{$match[1]}.{$match[2]}.summary.profile" ) ) {
265 $summary = json_decode(
266 file_get_contents( CODE_PROFILER_UPLOAD_DIR ."/{$match[1]}.{$match[2]}.summary.profile" ), true
267 );
268 }
269 if ( empty( $summary['memory'] ) ) { $summary['memory'] = '-'; }
270 if ( empty( $summary['queries'] ) ) { $summary['queries'] = '-'; }
271 if ( empty( $summary['time'] ) ) { $summary['time'] = '-'; }
272 if ( empty( $summary['items'] ) ) { $summary['items'] = '-'; }
273 if ( empty( $summary['io'] ) ) { $summary['io'] = '-'; }
274
275 $fsize += filesize( $path );
276 $profiles[$count]['ID'] = $match[1];
277 $profiles[$count]['profile'] = esc_html( $match[2] );
278 $profiles[$count]['date'] = filemtime( $path );
279 $profiles[$count]['mem'] = $summary['memory'];
280 $profiles[$count]['time'] = $summary['time'];
281 $profiles[$count]['queries'] = $summary['queries'];
282 $profiles[$count]['items'] = $summary['items'];
283 $profiles[$count]['io'] = $summary['io'];
284 $count++;
285 }
286 }
287 }
288 return $profiles;
289 }
290
291
292
293 /*
294 * Search a profile file for a string.
295 */
296 private function search_profile_file( $file, $string ) {
297
298 // Search in the filename too
299 if ( stripos( $file, $string ) !== false ) {
300 return true;
301 }
302
303 $fh = fopen( $file, 'rb' );
304 if ( $fh === false ) {
305 return false;
306 }
307 while(! feof( $fh ) ) {
308 $line = fgets( $fh );
309 // Remove the ABSPATH, we don't include it in the search
310 $line = ltrim( str_replace( $this->abspath, '', $line ), '\\/' );
311 if ( stripos( $line, $string ) !== false ) {
312 fclose( $fh );
313 return true;
314 }
315 }
316
317 fclose( $fh );
318 return false;
319 }
320
321
322 /*
323 * Delete one or more profiles.
324 */
325 public function delete_profiles( $profiles ) {
326
327 $error = false;
328 foreach( $profiles as $name ) {
329 $profile_name = code_profiler_get_profile_path( $name );
330 if ( $profile_name === false ) {
331 $error = true;
332 continue;
333 }
334 foreach( $this->default_files as $pname ) {
335 if ( file_exists( "$profile_name.$pname.profile" ) ) {
336 unlink( "$profile_name.$pname.profile" );
337 }
338 }
339 }
340 return $error;
341 }
342
343 }
344
345 // =====================================================================
346 // EOF
347