"Fossies" - the Fresh Open Source Software Archive

Member "phpMyAdmin-5.1.0-english/libraries/classes/Plugins/Import/ImportShp.php" (24 Feb 2021, 11027 Bytes) of package /linux/www/phpMyAdmin-5.1.0-english.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ImportShp.php" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.0.4-english_vs_5.1.0-english.

    1 <?php
    2 /**
    3  * ESRI Shape file import plugin for phpMyAdmin
    4  */
    5 
    6 declare(strict_types=1);
    7 
    8 namespace PhpMyAdmin\Plugins\Import;
    9 
   10 use PhpMyAdmin\File;
   11 use PhpMyAdmin\Gis\GisFactory;
   12 use PhpMyAdmin\Gis\GisMultiLineString;
   13 use PhpMyAdmin\Gis\GisMultiPoint;
   14 use PhpMyAdmin\Gis\GisPoint;
   15 use PhpMyAdmin\Gis\GisPolygon;
   16 use PhpMyAdmin\Import;
   17 use PhpMyAdmin\Message;
   18 use PhpMyAdmin\Plugins\ImportPlugin;
   19 use PhpMyAdmin\Properties\Plugins\ImportPluginProperties;
   20 use PhpMyAdmin\Sanitize;
   21 use PhpMyAdmin\ZipExtension;
   22 use ZipArchive;
   23 use const LOCK_EX;
   24 use function count;
   25 use function extension_loaded;
   26 use function file_exists;
   27 use function file_put_contents;
   28 use function mb_strlen;
   29 use function mb_substr;
   30 use function pathinfo;
   31 use function strcmp;
   32 use function strlen;
   33 use function substr;
   34 use function trim;
   35 use function unlink;
   36 
   37 /**
   38  * Handles the import for ESRI Shape files
   39  */
   40 class ImportShp extends ImportPlugin
   41 {
   42     /** @var ZipExtension */
   43     private $zipExtension;
   44 
   45     public function __construct()
   46     {
   47         parent::__construct();
   48         $this->setProperties();
   49         if (! extension_loaded('zip')) {
   50             return;
   51         }
   52 
   53         $this->zipExtension = new ZipExtension(new ZipArchive());
   54     }
   55 
   56     /**
   57      * Sets the import plugin properties.
   58      * Called in the constructor.
   59      *
   60      * @return void
   61      */
   62     protected function setProperties()
   63     {
   64         $importPluginProperties = new ImportPluginProperties();
   65         $importPluginProperties->setText(__('ESRI Shape File'));
   66         $importPluginProperties->setExtension('shp');
   67         $importPluginProperties->setOptions([]);
   68         $importPluginProperties->setOptionsText(__('Options'));
   69 
   70         $this->properties = $importPluginProperties;
   71     }
   72 
   73     /**
   74      * Handles the whole import logic
   75      *
   76      * @param array $sql_data 2-element array with sql data
   77      *
   78      * @return void
   79      */
   80     public function doImport(?File $importHandle = null, array &$sql_data = [])
   81     {
   82         global $db, $error, $finished, $import_file, $local_import_file, $message, $dbi;
   83 
   84         $GLOBALS['finished'] = false;
   85 
   86         if ($importHandle === null) {
   87             return;
   88         }
   89 
   90         /** @see ImportShp::readFromBuffer() */
   91         $GLOBALS['importHandle'] = $importHandle;
   92 
   93         $compression = $importHandle->getCompression();
   94 
   95         $shp = new ShapeFileImport(1);
   96         // If the zip archive has more than one file,
   97         // get the correct content to the buffer from .shp file.
   98         if ($compression === 'application/zip'
   99             && $this->zipExtension->getNumberOfFiles($import_file) > 1
  100         ) {
  101             if ($importHandle->openZip('/^.*\.shp$/i') === false) {
  102                 $message = Message::error(
  103                     __('There was an error importing the ESRI shape file: "%s".')
  104                 );
  105                 $message->addParam($importHandle->getError());
  106 
  107                 return;
  108             }
  109         }
  110 
  111         $temp_dbf_file = false;
  112         // We need dbase extension to handle .dbf file
  113         if (extension_loaded('dbase')) {
  114             $temp = $GLOBALS['PMA_Config']->getTempDir('shp');
  115             // If we can extract the zip archive to 'TempDir'
  116             // and use the files in it for import
  117             if ($compression === 'application/zip' && $temp !== null) {
  118                 $dbf_file_name = $this->zipExtension->findFile(
  119                     $import_file,
  120                     '/^.*\.dbf$/i'
  121                 );
  122                 // If the corresponding .dbf file is in the zip archive
  123                 if ($dbf_file_name) {
  124                     // Extract the .dbf file and point to it.
  125                     $extracted = $this->zipExtension->extract(
  126                         $import_file,
  127                         $dbf_file_name
  128                     );
  129                     if ($extracted !== false) {
  130                         // remove filename extension, e.g.
  131                         // dresden_osm.shp/gis.osm_transport_a_v06.dbf
  132                         // to
  133                         // dresden_osm.shp/gis.osm_transport_a_v06
  134                         $path_parts = pathinfo($dbf_file_name);
  135                         $dbf_file_name = $path_parts['dirname'] . '/' . $path_parts['filename'];
  136 
  137                         // sanitize filename
  138                         $dbf_file_name = Sanitize::sanitizeFilename($dbf_file_name, true);
  139 
  140                         // concat correct filename and extension
  141                         $dbf_file_path = $temp . '/' . $dbf_file_name . '.dbf';
  142 
  143                         if (file_put_contents($dbf_file_path, $extracted, LOCK_EX) !== false) {
  144                             $temp_dbf_file = true;
  145 
  146                             // Replace the .dbf with .*, as required by the bsShapeFiles library.
  147                             $shp->FileName = substr($dbf_file_path, 0, -4) . '.*';
  148                         }
  149                     }
  150                 }
  151             } elseif (! empty($local_import_file)
  152                 && ! empty($GLOBALS['cfg']['UploadDir'])
  153                 && $compression === 'none'
  154             ) {
  155                 // If file is in UploadDir, use .dbf file in the same UploadDir
  156                 // to load extra data.
  157                 // Replace the .shp with .*,
  158                 // so the bsShapeFiles library correctly locates .dbf file.
  159                 $file_name = mb_substr(
  160                     $import_file,
  161                     0,
  162                     mb_strlen($import_file) - 4
  163                 ) . '.*';
  164                 $shp->FileName = $file_name;
  165             }
  166         }
  167 
  168         // It should load data before file being deleted
  169         $shp->loadFromFile('');
  170 
  171         // Delete the .dbf file extracted to 'TempDir'
  172         if ($temp_dbf_file
  173             && isset($dbf_file_path)
  174             && @file_exists($dbf_file_path)
  175         ) {
  176             unlink($dbf_file_path);
  177         }
  178 
  179         if ($shp->lastError != '') {
  180             $error = true;
  181             $message = Message::error(
  182                 __('There was an error importing the ESRI shape file: "%s".')
  183             );
  184             $message->addParam($shp->lastError);
  185 
  186             return;
  187         }
  188 
  189         switch ($shp->shapeType) {
  190             // ESRI Null Shape
  191             case 0:
  192                 break;
  193             // ESRI Point
  194             case 1:
  195                 $gis_type = 'point';
  196                 break;
  197             // ESRI PolyLine
  198             case 3:
  199                 $gis_type = 'multilinestring';
  200                 break;
  201             // ESRI Polygon
  202             case 5:
  203                 $gis_type = 'multipolygon';
  204                 break;
  205             // ESRI MultiPoint
  206             case 8:
  207                 $gis_type = 'multipoint';
  208                 break;
  209             default:
  210                 $error = true;
  211                 $message = Message::error(
  212                     __('MySQL Spatial Extension does not support ESRI type "%s".')
  213                 );
  214                 $message->addParam($shp->getShapeName());
  215 
  216                 return;
  217         }
  218 
  219         if (isset($gis_type)) {
  220             /** @var GisMultiLineString|GisMultiPoint|GisPoint|GisPolygon $gis_obj */
  221             $gis_obj = GisFactory::factory($gis_type);
  222         } else {
  223             $gis_obj = null;
  224         }
  225 
  226         $num_rows = count($shp->records);
  227         // If .dbf file is loaded, the number of extra data columns
  228         $num_data_cols = $shp->getDBFHeader() !== null ? count($shp->getDBFHeader()) : 0;
  229 
  230         $rows = [];
  231         $col_names = [];
  232         if ($num_rows != 0) {
  233             foreach ($shp->records as $record) {
  234                 $tempRow = [];
  235                 if ($gis_obj == null) {
  236                     $tempRow[] = null;
  237                 } else {
  238                     $tempRow[] = "GeomFromText('"
  239                         . $gis_obj->getShape($record->SHPData) . "')";
  240                 }
  241 
  242                 if ($shp->getDBFHeader() !== null) {
  243                     foreach ($shp->getDBFHeader() as $c) {
  244                         $cell = trim((string) $record->DBFData[$c[0]]);
  245 
  246                         if (! strcmp($cell, '')) {
  247                             $cell = 'NULL';
  248                         }
  249 
  250                         $tempRow[] = $cell;
  251                     }
  252                 }
  253                 $rows[] = $tempRow;
  254             }
  255         }
  256 
  257         if (count($rows) === 0) {
  258             $error = true;
  259             $message = Message::error(
  260                 __('The imported file does not contain any data!')
  261             );
  262 
  263             return;
  264         }
  265 
  266         // Column names for spatial column and the rest of the columns,
  267         // if they are available
  268         $col_names[] = 'SPATIAL';
  269         for ($n = 0; $n < $num_data_cols; $n++) {
  270             $col_names[] = $shp->getDBFHeader()[$n][0];
  271         }
  272 
  273         // Set table name based on the number of tables
  274         if (strlen((string) $db) > 0) {
  275             $result = $dbi->fetchResult('SHOW TABLES');
  276             $table_name = 'TABLE ' . (count($result) + 1);
  277         } else {
  278             $table_name = 'TBL_NAME';
  279         }
  280         $tables = [
  281             [
  282                 $table_name,
  283                 $col_names,
  284                 $rows,
  285             ],
  286         ];
  287 
  288         // Use data from shape file to chose best-fit MySQL types for each column
  289         $analyses = [];
  290         $analyses[] = $this->import->analyzeTable($tables[0]);
  291 
  292         $table_no = 0;
  293         $spatial_col = 0;
  294         $analyses[$table_no][Import::TYPES][$spatial_col] = Import::GEOMETRY;
  295         $analyses[$table_no][Import::FORMATTEDSQL][$spatial_col] = true;
  296 
  297         // Set database name to the currently selected one, if applicable
  298         if (strlen((string) $db) > 0) {
  299             $db_name = $db;
  300             $options = ['create_db' => false];
  301         } else {
  302             $db_name = 'SHP_DB';
  303             $options = null;
  304         }
  305 
  306         // Created and execute necessary SQL statements from data
  307         $null_param = null;
  308         $this->import->buildSql($db_name, $tables, $analyses, $null_param, $options, $sql_data);
  309 
  310         unset($tables, $analyses);
  311 
  312         $finished = true;
  313         $error = false;
  314 
  315         // Commit any possible data in buffers
  316         $this->import->runQuery('', '', $sql_data);
  317     }
  318 
  319     /**
  320      * Returns specified number of bytes from the buffer.
  321      * Buffer automatically fetches next chunk of data when the buffer
  322      * falls short.
  323      * Sets $eof when $GLOBALS['finished'] is set and the buffer falls short.
  324      *
  325      * @param int $length number of bytes
  326      *
  327      * @return string
  328      */
  329     public static function readFromBuffer($length)
  330     {
  331         global $buffer, $eof, $importHandle;
  332 
  333         $import = new Import();
  334 
  335         if (strlen((string) $buffer) < $length) {
  336             if ($GLOBALS['finished']) {
  337                 $eof = true;
  338             } else {
  339                 $buffer .= $import->getNextChunk($importHandle);
  340             }
  341         }
  342         $result = substr($buffer, 0, $length);
  343         $buffer = substr($buffer, $length);
  344 
  345         return $result;
  346     }
  347 }