Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
afi
Pellicule
Commits
52d99129
Commit
52d99129
authored
Oct 14, 2020
by
Patrick Barroca
😁
Browse files
pre RT fix
parent
618ed0f2
Pipeline
#11347
passed with stage
in 40 seconds
Changes
21
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
155 additions
and
371 deletions
+155
-371
FEATURES/105493
FEATURES/105493
+0
-10
VERSIONS_WIP/105493
VERSIONS_WIP/105493
+0
-1
conf/http_server.template.php
conf/http_server.template.php
+0
-5
conf/persistence.template.php
conf/persistence.template.php
+1
-3
database/1.sql
database/1.sql
+22
-21
database/2.sql
database/2.sql
+0
-2
public/index.php
public/index.php
+2
-5
src/FileSystem.php
src/FileSystem.php
+32
-39
src/HttpClientAware.php
src/HttpClientAware.php
+2
-1
src/Models/Media.php
src/Models/Media.php
+9
-9
src/Models/Record.php
src/Models/Record.php
+5
-3
src/Providers/Electre.php
src/Providers/Electre.php
+7
-23
src/Providers/Orb.php
src/Providers/Orb.php
+8
-11
src/Providers/Provider.php
src/Providers/Provider.php
+13
-9
src/StormFileSystem.php
src/StormFileSystem.php
+9
-6
src/TimeSource.php
src/TimeSource.php
+0
-149
src/Traits/TimeSource.php
src/Traits/TimeSource.php
+6
-55
tests/FileSystemTest.php
tests/FileSystemTest.php
+8
-10
tests/MediaTest.php
tests/MediaTest.php
+4
-9
tests/TimeSourceForTest.php
tests/TimeSourceForTest.php
+27
-0
No files found.
FEATURES/105493
deleted
100644 → 0
View file @
618ed0f2
'105493' =>
['Label' => $this->_('utilisation dans le serveur de cache websvc'),
'Desc' => '',
'Image' => '',
'Video' => '',
'Category' => '',
'Right' => function($feature_description, $user) {return true;},
'Wiki' => '',
'Test' => '',
'Date' => '2020-07-10'],
\ No newline at end of file
VERSIONS_WIP/105493
deleted
100644 → 0
View file @
618ed0f2
- ticket #105493 : utilisation dans le serveur de cache websvc
\ No newline at end of file
conf/http_server.template.php
deleted
100644 → 0
View file @
618ed0f2
<?php
return
[
'URL_BASE'
=>
'https://localhost/pellicule'
];
?>
\ No newline at end of file
conf/persistence.template.php
View file @
52d99129
<?php
return
[
'base_url'
=>
'http://pellicule.afi-sa.net/'
,
'base_path'
=>
'./images'
];
\ No newline at end of file
return
[
'base_path'
=>
'./images'
];
\ No newline at end of file
database/1.sql
View file @
52d99129
create
table
if
not
exists
`records`
(
`id`
int
(
11
)
unsigned
not
null
auto_increment
,
`ean`
varchar
(
255
)
null
,
`isbn`
varchar
(
255
)
null
,
`ark`
varchar
(
255
)
null
,
`created_at`
datetime
not
null
,
`updated_at`
datetime
not
null
,
primary
key
(
`id`
),
key
`ean`
(
`ean`
),
key
`isbn`
(
`isbn`
),
key
`ark`
(
`ark`
)
)
engine
=
Aria
default
charset
=
utf8
;
`id`
int
(
11
)
unsigned
not
null
auto_increment
,
`ean`
varchar
(
255
)
null
,
`isbn`
varchar
(
255
)
null
,
`ark`
varchar
(
255
)
null
,
`created_at`
datetime
not
null
,
`updated_at`
datetime
not
null
,
primary
key
(
`id`
),
key
`ean`
(
`ean`
),
key
`isbn`
(
`isbn`
),
key
`ark`
(
`ark`
)
)
engine
=
Aria
default
charset
=
utf8
;
create
table
if
not
exists
`media`
(
`id`
int
(
11
)
unsigned
not
null
auto_increment
,
`record_id`
int
(
11
)
unsigned
not
null
,
`type`
varchar
(
255
)
not
null
,
`url`
varchar
(
255
)
not
null
,
`provider`
varchar
(
255
)
not
null
,
`created_at`
datetime
not
null
,
`updated_at`
datetime
not
null
,
primary
key
(
`id`
),
key
`record_id`
(
`record_id`
)
)
engine
=
Aria
default
charset
=
utf8
;
`id`
int
(
11
)
unsigned
not
null
auto_increment
,
`record_id`
int
(
11
)
unsigned
not
null
,
`type`
varchar
(
255
)
not
null
,
`url`
varchar
(
255
)
not
null
,
`provider`
varchar
(
255
)
not
null
,
`fullsize`
varchar
(
255
)
null
default
''
,
`created_at`
datetime
not
null
,
`updated_at`
datetime
not
null
,
primary
key
(
`id`
),
key
`record_id`
(
`record_id`
)
)
engine
=
Aria
default
charset
=
utf8
;
database/2.sql
deleted
100644 → 0
View file @
618ed0f2
alter
table
`media`
add
column
`fullsize`
varchar
(
255
)
null
default
''
;
public/index.php
View file @
52d99129
...
...
@@ -7,9 +7,7 @@ use \Storm\Persistence\SqlStrategy;
use
\
Pellicule\HttpClientAware
;
use
\
Pellicule\FileSystem
;
include_once
__DIR__
.
'/../conf/config.php'
;
require
__DIR__
.
'/../vendor/autoload.php'
;
require_once
__DIR__
.
'/../vendor/autoload.php'
;
$app
=
\
Pellicule\AppFactory
::
create
();
...
...
@@ -24,8 +22,7 @@ $app->add(function($request, $handler)
HttpClientAware
::
setHttpClientOptions
(
$settings
);
$settings
=
require
__DIR__
.
'/../conf/persistence.php'
;
\
Pellicule\FileSystem
::
setBaseUrl
(
$settings
[
'base_url'
]);
\
Pellicule\FileSystem
::
setBasePath
(
$settings
[
'base_path'
]);
FileSystem
::
setBasePath
(
$settings
[
'base_path'
]);
return
$handler
->
handle
(
$request
);
});
...
...
src/FileSystem.php
View file @
52d99129
...
...
@@ -2,26 +2,14 @@
namespace
Pellicule
;
use
\
Slim\Psr7\Factory\RequestFactory
;
use
\
Slim\Psr7\Factory\ResponseFactory
;
use
\
Fig\Http\Message\StatusCodeInterface
;
use
\
Storm\Collection
;
class
FileSystem
{
use
HttpClientAware
,
StormFileSystem
;
use
HttpClientAware
,
StormFileSystem
;
protected
static
$_base_path
=
'/tmp/'
,
$_base_url
;
public
static
function
setBaseUrl
(
$url_base
)
{
static
::
$_base_url
=
$url_base
;
}
public
static
function
baseUrl
(){
return
static
::
$_base_url
;
}
protected
static
$_base_path
=
'/tmp/'
;
public
static
function
setBasePath
(
$path
)
{
...
...
@@ -34,31 +22,34 @@ class FileSystem {
}
public
function
fileExists
(
$path
){
public
function
fileExists
(
$path
)
{
return
$this
->
getFileSystem
()
->
fileExists
(
$path
);
}
public
function
fileExtension
(
$response
){
foreach
(
$response
->
getHeader
(
'content-type'
)
as
$key
=>
$value
)
if
(
$extension
=
$this
->
_getExtensionFromContentType
(
$value
))
return
"."
.
$extension
;
public
function
fileExtension
(
$response
)
{
return
(
new
Collection
(
$response
->
getHeader
(
'content-type'
)))
->
collect
(
function
(
$value
)
{
return
'.'
.
$this
->
_getExtensionFromContentType
(
$value
);
})
->
detect
(
function
(
$value
)
{
return
'.'
!=
$value
;
});
}
protected
function
_getExtensionFromContentType
(
$value
){
$elements
=
explode
(
"/"
,
$value
);
if
(
isset
(
$elements
[
1
])
&&
in_array
(
$elements
[
1
],[
'jpg'
,
'bmp'
,
'gif'
,
'png'
,
'jpeg'
]))
return
$elements
[
1
];
return
""
;
protected
function
_getExtensionFromContentType
(
$value
)
{
$elements
=
explode
(
'/'
,
$value
);
return
(
isset
(
$elements
[
1
])
&&
in_array
(
$elements
[
1
],
[
'jpg'
,
'bmp'
,
'gif'
,
'png'
,
'jpeg'
]))
?
$elements
[
1
]
:
''
;
}
public
function
download
(
$media
)
{
$url
=
$media
->
getUrl
();
$path
=
$media
->
getFullsize
();
$headers
=
$media
->
getProviderHeaders
();
if
(
!
$url
||
!
$path
)
return
;
...
...
@@ -68,24 +59,26 @@ class FileSystem {
mkdir
(
$directory
,
0755
,
true
);
$request
=
(
new
RequestFactory
())
->
createRequest
(
'GET'
,
$url
);
if
(
$headers
)
foreach
(
$headers
as
$key
=>
$value
)
$request
=
$request
->
withHeader
(
$key
,
$value
);
$request
=
$this
->
_injectProviderHeaders
(
$request
,
$media
);
$client
=
$this
->
newFileGetContents
();
$response
=
$client
->
sendRequest
(
$request
);
$response
=
$client
->
sendRequest
(
$request
,
[
'timeout'
=>
4
]);
if
((
$status
=
$response
->
getStatusCode
())
!=
StatusCodeInterface
::
STATUS_OK
)
{
echo
$url
.
'file was not download error:'
.
$status
.
' '
.
$response
->
getBody
();
return
$this
;
if
(
StatusCodeInterface
::
STATUS_OK
!=
(
$status
=
$response
->
getStatusCode
()))
{
echo
$url
.
' file was not download error: '
.
$status
.
' '
.
$response
->
getBody
();
return
;
}
$extension
=
$this
->
fileExtension
(
$response
);
$this
->
getFileSystem
()
->
filePutContents
(
static
::
$_base_path
.
$path
.
$extension
,
$response
->
getBody
());
->
filePutContents
(
static
::
$_base_path
.
$path
.
$extension
,
$response
->
getBody
());
$media
->
setFullsize
(
$path
.
$extension
);
}
protected
function
_injectProviderHeaders
(
$request
,
$media
)
{
foreach
(
$media
->
getProviderHeaders
()
as
$key
=>
$value
)
$request
=
$request
->
withHeader
(
$key
,
$value
);
return
$request
;
}
}
src/HttpClientAware.php
View file @
52d99129
...
...
@@ -26,7 +26,8 @@ trait HttpClientAware {
if
(
static
::
$_default_http_client
)
return
static
::
$_default_http_client
;
return
new
FileGetContents
(
new
ResponseFactory
(),
[
'allow_redirects'
=>
true
]);
return
new
FileGetContents
(
new
ResponseFactory
(),
array_merge
(
static
::
_httpClientOptions
(),
[
'allow_redirects'
=>
true
]));
}
...
...
src/Models/Media.php
View file @
52d99129
...
...
@@ -2,11 +2,12 @@
namespace
Pellicule\Models
;
use
\
Storm\Model\ModelAbstract
;
use
\
Pellicule\StormFileSystem
;
use
\
Pellicule\Traits\TimeSource
;
class
Media
extends
ModelAbstract
{
use
\
Pellicule\StormFileSystem
,
\
Pellicule\Traits\TimeSource
;
use
StormFileSystem
,
TimeSource
;
const
SIZE_FULL
=
'fullsize'
,
...
...
@@ -17,8 +18,7 @@ class Media extends ModelAbstract {
$_table_name
=
'media'
,
$_belongs_to
=
[
'record'
=>
[
'model'
=>
Record
::
class
]
],
$_default_attribute_values
=
[
'id'
=>
0
,
'fullsize'
=>
''
,
$_default_attribute_values
=
[
'fullsize'
=>
''
,
'created_at'
=>
''
,
'updated_at'
=>
''
],
$_provider_headers
=
[];
...
...
@@ -58,12 +58,12 @@ class Media extends ModelAbstract {
}
public
function
setFullsizeFromIdentifier
(
$identifier
){
public
function
setFullsizeFromIdentifier
(
$identifier
)
{
return
$this
->
setFullsize
(
$this
->
_getFilePathFromIdentifier
(
$identifier
));
}
public
function
_getFilePathFromIdentifier
(
$identifier
=
""
)
{
public
function
_getFilePathFromIdentifier
(
$identifier
=
''
)
{
return
'/'
.
implode
(
'/'
,
array_merge
([
'images'
,
$this
->
getSize
(),
...
...
@@ -91,14 +91,14 @@ class Media extends ModelAbstract {
}
public
function
splitId
(
$identifier
=
""
)
{
public
function
splitId
(
$identifier
=
''
)
{
return
array_slice
(
str_split
(
sprintf
(
'%04d'
,
(
$identifier
?
$identifier
:
$this
->
getId
()))),
0
,
4
);
}
protected
function
getLocation
(
$identifier
=
""
)
{
protected
function
getLocation
(
$identifier
=
''
)
{
return
$this
->
_getFilePathFromIdentifier
(
$identifier
);
}
}
src/Models/Record.php
View file @
52d99129
...
...
@@ -2,9 +2,12 @@
namespace
Pellicule\Models
;
use
\
Storm\Model\ModelAbstract
;
use
\
Pellicule\Traits\TimeSource
;
class
Record
extends
ModelAbstract
{
use
TimeSource
;
protected
$_table_name
=
'records'
,
...
...
@@ -14,13 +17,12 @@ class Record extends ModelAbstract {
'role'
=>
'record'
]
],
$_default_attribute_values
=
[
'id'
=>
0
,
'created_at'
=>
''
,
$_default_attribute_values
=
[
'created_at'
=>
''
,
'updated_at'
=>
''
];
public
function
beforeSave
(){
$datenow
=
(
new
\
DateTime
())
->
format
(
'Y-m-d H:i:s'
);
$datenow
=
$this
->
getCurrentDateTime
(
);
if
(
!
$this
->
getCreatedAt
())
$this
->
setCreatedAt
(
$datenow
);
$this
->
setUpdatedAt
(
$datenow
);
...
...
src/Providers/Electre.php
View file @
52d99129
<?php
namespace
Pellicule\Providers
;
use
\
Buzz\Browser
;
use
\
Buzz\Client\FileGetContents
;
use
\
Slim\Psr7\Factory\ResponseFactory
;
use
\
Slim\Psr7\Factory\RequestFactory
;
use
\
Slim\Psr7\Request
;
use
\
Pellicule\Models\Record
;
use
\
Fig\Http\Message\StatusCodeInterface
;
...
...
@@ -19,8 +14,8 @@ class Electre extends Provider {
$_client_secret
;
public
function
__construct
(
$data
=
[]){
if
(
!
array_key_exists
(
'client_id'
,
$data
))
public
function
__construct
(
$data
=
[])
{
if
(
!
array_key_exists
(
'client_id'
,
$data
))
return
$this
;
$this
->
_client_id
=
$data
[
'client_id'
];
...
...
@@ -112,31 +107,20 @@ class Electre extends Provider {
}
protected
function
_getIsbnOrEan
(
$search_args
)
{
if
(
isset
(
$search_args
[
'ean'
]))
return
$search_args
[
'ean'
];
return
$search_args
[
'isbn'
];
}
protected
function
_getToken
(
$login
,
$secret
){
protected
function
_getToken
(
$id
,
$secret
)
{
$browser
=
static
::
newHttpClient
();
$response
=
$browser
->
post
(
static
::
TOKEN_END_POINT
.
'/connect/token'
,
[],
http_build_query
([
'client_id'
=>
$
login
,
http_build_query
([
'client_id'
=>
$
id
,
'client_secret'
=>
$secret
,
'grant_type'
=>
'client_credentials'
,
'scope'
=>
'webapi'
]));
'scope'
=>
'webapi'
]));
return
$response
;
}
public
function
_getAllLinks
(
$ean
)
{
$browser
=
static
::
newHttpClient
();
$response
=
$browser
->
get
(
static
::
API_END_POINT
.
'/v1.0/eans/'
.
$ean
,
$this
->
getHeaders
());
return
$response
;
return
static
::
newHttpClient
()
->
get
(
static
::
API_END_POINT
.
'/v1.0/eans/'
.
$ean
,
$this
->
getHeaders
());
}
}
src/Providers/Orb.php
View file @
52d99129
<?php
namespace
Pellicule\Providers
;
use
\
Buzz\Browser
;
use
\
Buzz\Client\FileGetContents
;
use
\
Slim\Psr7\Factory\ResponseFactory
;
use
\
Slim\Psr7\Factory\RequestFactory
;
use
\
Slim\Psr7\Request
;
use
\
Pellicule\Models\Record
;
use
\
Fig\Http\Message\StatusCodeInterface
;
class
Orb
extends
Provider
{
const
END_POINT
=
'https://api.base-orb.fr'
;
const
END_POINT
=
'https://api.base-orb.fr'
;
protected
$_username
,
$_secret
;
public
function
__construct
(
$data
=
[]){
public
function
__construct
(
$data
=
[])
{
if
(
!
array_key_exists
(
'username'
,
$data
))
return
$this
;
...
...
@@ -67,9 +62,11 @@ class Orb extends Provider {
$images
=
$json_content
[
'data'
][
0
][
'images'
];
$medialist
=
[];
$imagetype_translate
=
[
'front'
=>
'cover'
,
'back'
=>
'back_cover'
,
'additional'
=>
'extract'
];
foreach
(
array_keys
(
$images
)
as
$image_type
){
$medialist
[]
=
(
new
\
Pellicule\Models\Media
())
$imagetype_translate
=
[
'front'
=>
'cover'
,
'back '
=>
'back_cover'
,
'additional'
=>
'extract'
];
foreach
(
array_keys
(
$images
)
as
$image_type
)
{
$medialist
[]
=
(
new
\
Pellicule\Models\Media
())
->
setProvider
(
$this
->
providerName
())
->
setType
(
$imagetype_translate
[
$image_type
])
->
setUrl
(
$images
[
$image_type
][
'original'
][
'src'
])
...
...
src/Providers/Provider.php
View file @
52d99129
...
...
@@ -4,7 +4,8 @@ namespace Pellicule\Providers;
use
\
Pellicule\HttpClientAware
;
use
\
Pellicule\Models\Record
;
use
\
Pellicule\Providers\FetchRecordResult
;
use
Fig\Http\Message\StatusCodeInterface
;
use
\
Fig\Http\Message\StatusCodeInterface
;
use
\
Storm\Collection
;
...
...
@@ -31,7 +32,7 @@ abstract class Provider {
if
(
!
$request
->
hasHeader
(
'Authorization'
))
return
new
Error
(
StatusCodeInterface
::
STATUS_NOT_FOUND
,
'record not found'
);
$credentials
=
array_filter
((
new
\
Storm\
Collection
(
$request
->
getHeader
(
'Authorization'
)))
$credentials
=
array_filter
((
new
Collection
(
$request
->
getHeader
(
'Authorization'
)))
->
select
(
function
(
$header
)
{
return
(
$header
&&
'Pellicule '
==
substr
(
$header
,
0
,
10
));
...
...
@@ -65,10 +66,9 @@ abstract class Provider {
protected
function
_getIsbnOrEan
(
$search_args
)
{
if
(
isset
(
$search_args
[
'ean'
]))
return
$search_args
[
'ean'
];
return
$search_args
[
'isbn'
];
return
(
isset
(
$search_args
[
'ean'
]))
?
$search_args
[
'ean'
]
:
$search_args
[
'isbn'
];
}
...
...
@@ -79,17 +79,21 @@ abstract class Provider {
public
function
setSearchArgs
(
$args
)
{
if
(
isset
(
$args
[
'isbn'
])
||
isset
(
$args
[
'ean'
]))
$this
->
_search_args
=
$args
;
$this
->
_search_args
=
$args
;
return
$this
;
}
public
function
setHeaders
(
$value
){
public
function
setHeaders
(
$value
)
{
$this
->
_headers
=
$value
;
}
public
function
getHeaders
(){
public
function
getHeaders
()
{
return
$this
->
_headers
;
}
abstract
public
function
fetchRecord
();
}
\ No newline at end of file
src/StormFileSystem.php
View file @
52d99129
<?php
namespace
Pellicule
;
use
\
Storm\FileSystem\Disk
;
use
\
Pellicule\FileSystem
;
trait
StormFileSystem
{
protected
static
$_file_system
;
...
...
@@ -11,15 +14,15 @@ trait StormFileSystem {
public
static
function
getFileSystem
()
{
if
(
null
!==
self
::
$_file_system
)
return
self
::
$_file_system
;
return
new
\
Storm\FileSystem\
Disk
();
return
(
null
!==
self
::
$_file_system
)
?
self
::
$_file_system
:
new
Disk
();
}
public
static
function
getPelliculeFileSystem
()
{
if
(
null
!==
self
::
$_file_system
)
return
self
::
$_file_system
;
return
new
\
Pellicule\
FileSystem
();
return
(
null
!==
self
::
$_file_system
)
?
self
::
$_file_system
:
new
FileSystem
();
}
}
\ No newline at end of file
src/TimeSource.php
View file @
52d99129
<?php
/**
* Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
*
* BOKEH is free software; you can redistribute it and/or modify
* it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
* the Free Software Foundation.
*
* There are special exceptions to the terms and conditions of the AGPL as it
* is applied to this software (see README file).
*
* BOKEH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
* along with BOKEH; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Encapsule les appels systeme temps pour pouvoir les surcharger pendant les tests
* @category testing
*/
namespace
Pellicule
;
class
TimeSource
{
const
DAY_AND_HOURS_FORMAT
=
'Y-m-d H:i:s'
;
/** @return int */
public
function
time
()
{
return
time
();
}
public
function
dateYmd
()
{
return
$this
->
dateFormat
(
'Y-m-d'
);
}
public
function
dateDayAndHours
()
{
return
$this
->
dateFormat
(
static
::
DAY_AND_HOURS_FORMAT
);
}
public
function
dateDayMonthYear
()
{
return
$this
->
dateFormat
(
'dmY'
);
}
public
function
dateFormat
(
$format
)
{
return
date
(
$format
,
$this
->
time
());
}
public
function
dateHttpHeader
()
{
return
gmdate
(
'D, d M Y H:i:s \G\M\T'
,
$this
->
time
());
}
public
function
date
()
{
$time
=
$this
->
time
();
return
$this
->
midnightTime
(
date
(
'n'
,
$time
),
date
(
'j'
,
$time
),
date
(
'Y'
,
$time
));
}
public
function
mktime
(
$hour
,
$minute
,
$second
,
$month
,
$day
,
$year
)
{
return
mktime
(
$hour
,
$minute
,
$second
,
$month
,
$day
,
$year
);
}
public
function
nextDate
()
{
$time
=
$this
->
time
();
return
$this
->
midnightTime
(
date
(
'n'
,
$time
),
date
(
'j'
,
$time
)
+
1
,
date
(
'Y'
,
$time
));
}
public
function
nextMonths
(
$number_of_months
=
1
)
{
$time
=
$this
->
time
();
return
$this
->
midnightTime
(
date
(
'n'
,
$time
)
+
$number_of_months
,
date
(
'j'
,
$time
),
date
(
'Y'
,
$time
));
}
public
function
getMonth
(
$month
)
{
$time
=
$this
->
time
();
$year
=
date
(
'm'
,
$time
)
>
$month
?
date
(
'Y'
,
$time
)
+
1
:
date
(
'Y'
,
$time
);
return
date
(
'Y-m'
,
$this
->
midnightTime
(
$month
,
1
,
$year
));
}
protected
function
midnightTime
(
$month
,
$day
,
$year
)
{
return
mktime
(
0
,
0
,
0
,
$month
,
$day
,
$year
);
}
public
function
lastYear
()
{
$time
=
$this
->
time
();
return
date
(
'Y'
,
$time
)
-
1
.
date
(
'-m-d'
,
$time
);
}
public
function
daysFrom
(
$time
)
{
return
(
int
)(
$this
->
hoursFrom
(
$time
)
/
24
);
}
public
function
hoursFrom
(
$time
)
{
return
(
int
)((
$this
->
time
()
-
$time
)
/
3660
);
}