1<?php 2/** 3 * CodeIgniter 4 * 5 * An open source application development framework for PHP 6 * 7 * This content is released under the MIT License (MIT) 8 * 9 * Copyright (c) 2014 - 2018, British Columbia Institute of Technology 10 * 11 * Permission is hereby granted, free of charge, to any person obtaining a copy 12 * of this software and associated documentation files (the "Software"), to deal 13 * in the Software without restriction, including without limitation the rights 14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 * copies of the Software, and to permit persons to whom the Software is 16 * furnished to do so, subject to the following conditions: 17 * 18 * The above copyright notice and this permission notice shall be included in 19 * all copies or substantial portions of the Software. 20 * 21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 * THE SOFTWARE. 28 * 29 * @package CodeIgniter 30 * @author EllisLab Dev Team 31 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) 32 * @copyright Copyright (c) 2014 - 2018, British Columbia Institute of Technology (http://bcit.ca/) 33 * @license http://opensource.org/licenses/MIT MIT License 34 * @link https://codeigniter.com 35 * @since Version 1.3.1 36 * @filesource 37 */ 38defined('BASEPATH') OR exit('No direct script access allowed'); 39 40/** 41 * HTML Table Generating Class 42 * 43 * Lets you create tables manually or from database result objects, or arrays. 44 * 45 * @package CodeIgniter 46 * @subpackage Libraries 47 * @category HTML Tables 48 * @author EllisLab Dev Team 49 * @link https://codeigniter.com/user_guide/libraries/table.html 50 */ 51class CI_Table { 52 53 /** 54 * Data for table rows 55 * 56 * @var array 57 */ 58 public $rows = array(); 59 60 /** 61 * Data for table heading 62 * 63 * @var array 64 */ 65 public $heading = array(); 66 67 /** 68 * Whether or not to automatically create the table header 69 * 70 * @var bool 71 */ 72 public $auto_heading = TRUE; 73 74 /** 75 * Table caption 76 * 77 * @var string 78 */ 79 public $caption = NULL; 80 81 /** 82 * Table layout template 83 * 84 * @var array 85 */ 86 public $template = NULL; 87 88 /** 89 * Newline setting 90 * 91 * @var string 92 */ 93 public $newline = "\n"; 94 95 /** 96 * Contents of empty cells 97 * 98 * @var string 99 */ 100 public $empty_cells = ''; 101 102 /** 103 * Callback for custom table layout 104 * 105 * @var function 106 */ 107 public $function = NULL; 108 109 /** 110 * Set the template from the table config file if it exists 111 * 112 * @param array $config (default: array()) 113 * @return void 114 */ 115 public function __construct($config = array()) 116 { 117 // initialize config 118 foreach ($config as $key => $val) 119 { 120 $this->template[$key] = $val; 121 } 122 123 log_message('info', 'Table Class Initialized'); 124 } 125 126 // -------------------------------------------------------------------- 127 128 /** 129 * Set the template 130 * 131 * @param array $template 132 * @return bool 133 */ 134 public function set_template($template) 135 { 136 if ( ! is_array($template)) 137 { 138 return FALSE; 139 } 140 141 $this->template = $template; 142 return TRUE; 143 } 144 145 // -------------------------------------------------------------------- 146 147 /** 148 * Set the table heading 149 * 150 * Can be passed as an array or discreet params 151 * 152 * @param mixed 153 * @return CI_Table 154 */ 155 public function set_heading($args = array()) 156 { 157 $this->heading = $this->_prep_args(func_get_args()); 158 return $this; 159 } 160 161 // -------------------------------------------------------------------- 162 163 /** 164 * Set columns. Takes a one-dimensional array as input and creates 165 * a multi-dimensional array with a depth equal to the number of 166 * columns. This allows a single array with many elements to be 167 * displayed in a table that has a fixed column count. 168 * 169 * @param array $array 170 * @param int $col_limit 171 * @return array 172 */ 173 public function make_columns($array = array(), $col_limit = 0) 174 { 175 if ( ! is_array($array) OR count($array) === 0 OR ! is_int($col_limit)) 176 { 177 return FALSE; 178 } 179 180 // Turn off the auto-heading feature since it's doubtful we 181 // will want headings from a one-dimensional array 182 $this->auto_heading = FALSE; 183 184 if ($col_limit === 0) 185 { 186 return $array; 187 } 188 189 $new = array(); 190 do 191 { 192 $temp = array_splice($array, 0, $col_limit); 193 194 if (count($temp) < $col_limit) 195 { 196 for ($i = count($temp); $i < $col_limit; $i++) 197 { 198 $temp[] = ' '; 199 } 200 } 201 202 $new[] = $temp; 203 } 204 while (count($array) > 0); 205 206 return $new; 207 } 208 209 // -------------------------------------------------------------------- 210 211 /** 212 * Set "empty" cells 213 * 214 * Can be passed as an array or discreet params 215 * 216 * @param mixed $value 217 * @return CI_Table 218 */ 219 public function set_empty($value) 220 { 221 $this->empty_cells = $value; 222 return $this; 223 } 224 225 // -------------------------------------------------------------------- 226 227 /** 228 * Add a table row 229 * 230 * Can be passed as an array or discreet params 231 * 232 * @param mixed 233 * @return CI_Table 234 */ 235 public function add_row($args = array()) 236 { 237 $this->rows[] = $this->_prep_args(func_get_args()); 238 return $this; 239 } 240 241 // -------------------------------------------------------------------- 242 243 /** 244 * Prep Args 245 * 246 * Ensures a standard associative array format for all cell data 247 * 248 * @param array 249 * @return array 250 */ 251 protected function _prep_args($args) 252 { 253 // If there is no $args[0], skip this and treat as an associative array 254 // This can happen if there is only a single key, for example this is passed to table->generate 255 // array(array('foo'=>'bar')) 256 if (isset($args[0]) && count($args) === 1 && is_array($args[0]) && ! isset($args[0]['data'])) 257 { 258 $args = $args[0]; 259 } 260 261 foreach ($args as $key => $val) 262 { 263 is_array($val) OR $args[$key] = array('data' => $val); 264 } 265 266 return $args; 267 } 268 269 // -------------------------------------------------------------------- 270 271 /** 272 * Add a table caption 273 * 274 * @param string $caption 275 * @return CI_Table 276 */ 277 public function set_caption($caption) 278 { 279 $this->caption = $caption; 280 return $this; 281 } 282 283 // -------------------------------------------------------------------- 284 285 /** 286 * Generate the table 287 * 288 * @param mixed $table_data 289 * @return string 290 */ 291 public function generate($table_data = NULL) 292 { 293 // The table data can optionally be passed to this function 294 // either as a database result object or an array 295 if ( ! empty($table_data)) 296 { 297 if ($table_data instanceof CI_DB_result) 298 { 299 $this->_set_from_db_result($table_data); 300 } 301 elseif (is_array($table_data)) 302 { 303 $this->_set_from_array($table_data); 304 } 305 } 306 307 // Is there anything to display? No? Smite them! 308 if (empty($this->heading) && empty($this->rows)) 309 { 310 return 'Undefined table data'; 311 } 312 313 // Compile and validate the template date 314 $this->_compile_template(); 315 316 // Validate a possibly existing custom cell manipulation function 317 if (isset($this->function) && ! is_callable($this->function)) 318 { 319 $this->function = NULL; 320 } 321 322 // Build the table! 323 324 $out = $this->template['table_open'].$this->newline; 325 326 // Add any caption here 327 if ($this->caption) 328 { 329 $out .= '<caption>'.$this->caption.'</caption>'.$this->newline; 330 } 331 332 // Is there a table heading to display? 333 if ( ! empty($this->heading)) 334 { 335 $out .= $this->template['thead_open'].$this->newline.$this->template['heading_row_start'].$this->newline; 336 337 foreach ($this->heading as $heading) 338 { 339 $temp = $this->template['heading_cell_start']; 340 341 foreach ($heading as $key => $val) 342 { 343 if ($key !== 'data') 344 { 345 $temp = str_replace('<th', '<th '.$key.'="'.$val.'"', $temp); 346 } 347 } 348 349 $out .= $temp.(isset($heading['data']) ? $heading['data'] : '').$this->template['heading_cell_end']; 350 } 351 352 $out .= $this->template['heading_row_end'].$this->newline.$this->template['thead_close'].$this->newline; 353 } 354 355 // Build the table rows 356 if ( ! empty($this->rows)) 357 { 358 $out .= $this->template['tbody_open'].$this->newline; 359 360 $i = 1; 361 foreach ($this->rows as $row) 362 { 363 if ( ! is_array($row)) 364 { 365 break; 366 } 367 368 // We use modulus to alternate the row colors 369 $name = fmod($i++, 2) ? '' : 'alt_'; 370 371 $out .= $this->template['row_'.$name.'start'].$this->newline; 372 373 foreach ($row as $cell) 374 { 375 $temp = $this->template['cell_'.$name.'start']; 376 377 foreach ($cell as $key => $val) 378 { 379 if ($key !== 'data') 380 { 381 $temp = str_replace('<td', '<td '.$key.'="'.$val.'"', $temp); 382 } 383 } 384 385 $cell = isset($cell['data']) ? $cell['data'] : ''; 386 $out .= $temp; 387 388 if ($cell === '' OR $cell === NULL) 389 { 390 $out .= $this->empty_cells; 391 } 392 elseif (isset($this->function)) 393 { 394 $out .= call_user_func($this->function, $cell); 395 } 396 else 397 { 398 $out .= $cell; 399 } 400 401 $out .= $this->template['cell_'.$name.'end']; 402 } 403 404 $out .= $this->template['row_'.$name.'end'].$this->newline; 405 } 406 407 $out .= $this->template['tbody_close'].$this->newline; 408 } 409 410 $out .= $this->template['table_close']; 411 412 // Clear table class properties before generating the table 413 $this->clear(); 414 415 return $out; 416 } 417 418 // -------------------------------------------------------------------- 419 420 /** 421 * Clears the table arrays. Useful if multiple tables are being generated 422 * 423 * @return CI_Table 424 */ 425 public function clear() 426 { 427 $this->rows = array(); 428 $this->heading = array(); 429 $this->auto_heading = TRUE; 430 return $this; 431 } 432 433 // -------------------------------------------------------------------- 434 435 /** 436 * Set table data from a database result object 437 * 438 * @param CI_DB_result $object Database result object 439 * @return void 440 */ 441 protected function _set_from_db_result($object) 442 { 443 // First generate the headings from the table column names 444 if ($this->auto_heading === TRUE && empty($this->heading)) 445 { 446 $this->heading = $this->_prep_args($object->list_fields()); 447 } 448 449 foreach ($object->result_array() as $row) 450 { 451 $this->rows[] = $this->_prep_args($row); 452 } 453 } 454 455 // -------------------------------------------------------------------- 456 457 /** 458 * Set table data from an array 459 * 460 * @param array $data 461 * @return void 462 */ 463 protected function _set_from_array($data) 464 { 465 if ($this->auto_heading === TRUE && empty($this->heading)) 466 { 467 $this->heading = $this->_prep_args(array_shift($data)); 468 } 469 470 foreach ($data as &$row) 471 { 472 $this->rows[] = $this->_prep_args($row); 473 } 474 } 475 476 // -------------------------------------------------------------------- 477 478 /** 479 * Compile Template 480 * 481 * @return void 482 */ 483 protected function _compile_template() 484 { 485 if ($this->template === NULL) 486 { 487 $this->template = $this->_default_template(); 488 return; 489 } 490 491 $this->temp = $this->_default_template(); 492 foreach (array('table_open', 'thead_open', 'thead_close', 'heading_row_start', 'heading_row_end', 'heading_cell_start', 'heading_cell_end', 'tbody_open', 'tbody_close', 'row_start', 'row_end', 'cell_start', 'cell_end', 'row_alt_start', 'row_alt_end', 'cell_alt_start', 'cell_alt_end', 'table_close') as $val) 493 { 494 if ( ! isset($this->template[$val])) 495 { 496 $this->template[$val] = $this->temp[$val]; 497 } 498 } 499 } 500 501 // -------------------------------------------------------------------- 502 503 /** 504 * Default Template 505 * 506 * @return array 507 */ 508 protected function _default_template() 509 { 510 return array( 511 'table_open' => '<table border="0" cellpadding="4" cellspacing="0">', 512 513 'thead_open' => '<thead>', 514 'thead_close' => '</thead>', 515 516 'heading_row_start' => '<tr>', 517 'heading_row_end' => '</tr>', 518 'heading_cell_start' => '<th>', 519 'heading_cell_end' => '</th>', 520 521 'tbody_open' => '<tbody>', 522 'tbody_close' => '</tbody>', 523 524 'row_start' => '<tr>', 525 'row_end' => '</tr>', 526 'cell_start' => '<td>', 527 'cell_end' => '</td>', 528 529 'row_alt_start' => '<tr>', 530 'row_alt_end' => '</tr>', 531 'cell_alt_start' => '<td>', 532 'cell_alt_end' => '</td>', 533 534 'table_close' => '</table>' 535 ); 536 } 537 538} 539