Skip to content

Commit

Permalink
added basic Datapackage, Resource and DataStream objects (+travis, co…
Browse files Browse the repository at this point in the history
…veralls, phpunit etc..) (#3)
  • Loading branch information
OriHoch authored Apr 17, 2017
1 parent 3bfa2ad commit c0598aa
Show file tree
Hide file tree
Showing 18 changed files with 630 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .coveralls.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
coverage_clover: coverage-clover.xml
json_path: coveralls.json
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/vendor/
/.idea/
/coverage-clover.xml
/composer.lock
15 changes: 15 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
language: php
php:
- '5.4'
- '5.5'
- '5.6'
- '7.0'
- '7.1'
- nightly
- hhvm
before_script:
- composer install --prefer-dist
script:
- composer test
after_success:
- vendor/bin/coveralls
23 changes: 23 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Contributing

The project follows the [Open Knowledge International coding standards](https://github.com/okfn/coding-standards).


## Getting Started

1. Clone the repo
2. Run the tests
```
$ composer install
$ composer test
```

## Phpunit - for unit tests

Phpunit is used for unit tests, you can find the tests under tests directory

Running Phpunit directly: `vendor/bin/phunit`

## Coveralls - for coverage

when running `composer test` phpunit generates coverage report in coverage-clover.xml - this is then sent to Coveralls via Travis.
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,40 @@

[![Travis](https://travis-ci.org/frictionlessdata/datapackage-php.svg?branch=master)](https://travis-ci.org/frictionlessdata/datapackage-php)
[![Coveralls](http://img.shields.io/coveralls/frictionlessdata/datapackage-php.svg?branch=master)](https://coveralls.io/r/frictionlessdata/datapackage-php?branch=master)
[![Packagist](https://img.shields.io/packagist/dm/oki/datapackage.svg)](https://packagist.org/packages/oki/datapackage)
[![Packagist](https://img.shields.io/packagist/dm/frictionlessdata/datapackage.svg)](https://packagist.org/packages/frictionlessdata/datapackage)
[![SemVer](https://img.shields.io/badge/versions-SemVer-brightgreen.svg)](http://semver.org/)
[![Gitter](https://img.shields.io/gitter/room/frictionlessdata/chat.svg)](https://gitter.im/frictionlessdata/chat)

A utility library for working with [Data Package](https://specs.frictionlessdata.io/data-package/) in php.
A utility library for working with [Data Package](https://specs.frictionlessdata.io/data-package/) in PHP.


## Getting Started

### Installation

```bash
$ composer require frictionlessdata/datapackage
```

### Usage

```php
use frictionlessdata\datapackage;

$datapackage = new Datapackage("tests/fixtures/multi_data_datapackage.json");
foreach ($datapackage as $resource) {
print("-- ".$resource->name()." --");
$i = 0;
foreach ($resource as $dataStream) {
print("-dataStream ".++$i);
foreach ($dataStream as $line) {
print($line);
}
}
}
```


## Contributing

Please read the contribution guidelines: [How to Contribute](CONTRIBUTING.md)
20 changes: 20 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "frictionlessdata/datapackage",
"description": "A utility library for working with Data Packages",
"license": "MIT",
"require": {
"php": ">=5.4"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35",
"satooshi/php-coveralls": "^1.0"
},
"autoload": {
"psr-4": {
"frictionlessdata\\datapackage\\": "src/"
}
},
"scripts": {
"test": "phpunit --debug tests/ --coverage-clover coverage-clover.xml"
}
}
53 changes: 53 additions & 0 deletions src/DataStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php namespace frictionlessdata\datapackage;


class DataStream implements \Iterator
{
protected $_currentLineNumber = 0;
protected $_fopenResource;
protected $_dataSource;

public function __construct($dataSource)
{
try {
$this->_fopenResource = fopen($dataSource, "r");
} catch (\Exception $e) {
throw new DataStreamOpenException("Failed to open source ".json_encode($dataSource));
}
}

public function __destruct()
{
fclose($this->_fopenResource);
}

public function rewind() {
if ($this->_currentLineNumber != 0) {
throw new \Exception("DataStream does not support rewind, sorry");
}
}

public function current() {
$line = fgets($this->_fopenResource);
if ($line === false) {
return "";
} else {
return $line;
}
}

public function key() {
return $this->_currentLineNumber;
}

public function next() {
$this->_currentLineNumber++;
}

public function valid() {
return (!feof($this->_fopenResource));
}
}


class DataStreamOpenException extends \Exception {};
89 changes: 89 additions & 0 deletions src/Datapackage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php namespace frictionlessdata\datapackage;


/**
* Datapackage representation, supports loading from the following sources:
* - native PHP object containing the descriptor
* - JSON encoded object
* - URL (must be in either 'http' or 'https' schemes)
* - local filesystem (POSIX) path
*/
class Datapackage implements \Iterator
{
protected $_descriptor;
protected $_currentResourcePosition = 0;
protected $_basePath;

public function __construct($source, $basePath=null)
{
if (is_object($source)) {
$this->_descriptor = $source;
$this->_basePath = $basePath;
} elseif (is_string($source)) {
if (Utils::is_json_string($source)) {
try {
$this->_descriptor = json_decode($source);
} catch (\Exception $e) {
throw new DatapackageInvalidSourceException("Failed to load source: ".json_encode($source).": ".$e->getMessage());
}
$this->_basePath = $basePath;
} elseif ($this->_isHttpSource($source)) {
try {
$this->_descriptor = json_decode(file_get_contents($this->_normalizeHttpSource($source)));
} catch (\Exception $e) {
throw new DatapackageInvalidSourceException("Failed to load source: ".json_encode($source).": ".$e->getMessage());
}
// http sources don't allow relative paths, hence basePath should remain null
$this->_basePath = null;
} else {
if (empty($basePath)) {
$this->_basePath = dirname($source);
} else {
$this->_basePath = $basePath;
$absPath = $this->_basePath.DIRECTORY_SEPARATOR.$source;
if (file_exists($absPath)) {
$source = $absPath;
}
}
try {
$this->_descriptor = json_decode(file_get_contents($source));
} catch (\Exception $e) {
throw new DatapackageInvalidSourceException("Failed to load source: ".json_encode($source).": ".$e->getMessage());
}

}
} else {
throw new DatapackageInvalidSourceException("Invalid source: ".json_encode($source));
}
}

protected function _normalizeHttpSource($source)
{
return $source;
}

protected function _isHttpSource($source)
{
return Utils::is_http_source($source);
}

protected function _initResource($resourceDescriptor)
{
return new Resource($resourceDescriptor, $this->_basePath);
}

public function descriptor()
{
return $this->_descriptor;
}

// standard iterator functions - to iterate over the resources
public function rewind() { $this->_currentResourcePosition = 0; }
public function current() { return $this->_initResource($this->descriptor()->resources[$this->_currentResourcePosition]); }
public function key() { return $this->_currentResourcePosition; }
public function next() { $this->_currentResourcePosition++; }
public function valid() { return isset($this->descriptor()->resources[$this->_currentResourcePosition]); }
}


class DatapackageInvalidSourceException extends \Exception {};
54 changes: 54 additions & 0 deletions src/Resource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php namespace frictionlessdata\datapackage;


class Resource implements \Iterator
{
protected $_descriptor;
protected $_basePath;
protected $_currentDataPosition = 0;

public function __construct($descriptor, $basePath)
{
$this->_basePath = $basePath;
$this->_descriptor = $descriptor;
}

protected function _isHttpSource($dataSource)
{
return Utils::is_http_source($dataSource);
}

protected function _normalizeDataSource($dataSource)
{
if (!empty($this->_basePath) && !Utils::is_http_source($dataSource)) {
// TODO: support JSON pointers
$absPath = $this->_basePath.DIRECTORY_SEPARATOR.$dataSource;
if (file_exists($absPath)) {
$dataSource = $absPath;
}
}
return $dataSource;
}

protected function _getDataStream($dataSource)
{
return new DataStream($this->_normalizeDataSource($dataSource));
}

public function descriptor()
{
return $this->_descriptor;
}

public function name()
{
return $this->descriptor()->name;
}

// standard iterator functions - to iterate over the data sources
public function rewind() { $this->_currentDataPosition = 0; }
public function current() { return $this->_getDataStream($this->descriptor()->data[$this->_currentDataPosition]); }
public function key() { return $this->_currentDataPosition; }
public function next() { $this->_currentDataPosition++; }
public function valid() { return isset($this->descriptor()->data[$this->_currentDataPosition]); }
}
26 changes: 26 additions & 0 deletions src/Utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php namespace frictionlessdata\datapackage;


class Utils
{

public static function is_json_string($json)
{
return (
is_string($json)
&& (strpos(ltrim($json), "{") === 0)
);
}

public static function is_http_source($source)
{
return (
is_string($source)
&& (
strpos($source, "http:") === 0
|| strpos($source, "https:") === 0
)
);
}

}
Loading

0 comments on commit c0598aa

Please sign in to comment.