Related
I recently found this laravel package on github and after installing it, I noticed that the setLocale() function in Translation.php class didn't work as expected. Many issues have been opened about this bug and I started trying to fix it as the maintainer's time was limited due to other open source projects : (Adldap2/Adldap2 and Adldap2/Adldap2-Laravel as stated here.
In Translation.php class, I first changed getLocale() function from
public function getLocale()
{
if ($this->request->hasCookie('locale')) {
return $this->request->cookie('locale');
} else {
return $this->getConfigDefaultLocale();
}
}
to
public function getLocale()
{
if ($this->request->session()->has('locale')) {
return $this->request->session()->get('locale');
} else {
return $this->getConfigDefaultLocale();
}
}
because the comments in the Translation contract specified that sessions were to be used for the getLocale() function.
Then, after inserting some text to translate with the provided helper function {{_t('text')}} in my blade templates, I dumped the result in my localization middleware to see if everything was fine with dd(Translate::getLocale).
I got a 'Session store not set on request.' error. After some googling, I found this thread on Stack Overflow and decided to use the helper function session() and later the Session facade as I found it more convenient.
After these operations, dd(Translate::getLocale) worked fine and I proceeded to modify setLocale() function from
public function setLocale($code = '')
{
$this->locale = $code;
}
to
public function setLocale($code = '')
{
$this->locale = $code;
$this->request->session()->put('locale', $locale);
}
and lastly
public function setLocale($code = '')
{
$this->locale = $code;
session(['locale' => $this->locale]);
}
in order to avoid the 'Session store not set on request' error.
Everything worked correctly.
Despite these actions, the translation still didn't work as expected.
Then, I dumped Translation::getLocale() first in my localization middleware and then in my Translation.php class and I got two different results : 'en' for the Translation.php class and 'fr'(Automatically detected by the middleware).
I also added automatic locale detection to the Translation following this code snippet and tried again with no success.
Finally, I dumped the session variable with dd(Session::all) in the two files and got this from Translation.php class :
array:4 [▼
"locale" => "en"
"_token" => "3FYWDEaPk47ZcqIS2u5KzGmwz7vI0x3G8h9GkNZP"
"_previous" => array:1 [▶]
"_flash" => array:2 [▶]
]
and that from localization middleware :
array:4 [▼
"locale" => "fr"
]
After checking my database where my sessions are stored, I found only one session, so I don't understand why this is happening.
I also checked some blog posts about laravel service container as $app->make() have been used in Translation class constructor, but I'm stuck.
Here are my current Translation.php and localization middleware :
<?php
namespace App\Http\Middleware;
use Closure;
use Carbon\Carbon;
use Session;
use App;
use Config;
use Translation;
use Illuminate\Http\Request;
class Localization
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ( Session::has('locale')) {
$locale = Session::get('locale', Config::get('app.locale'));
} else {
$locale = substr($request->server('HTTP_ACCEPT_LANGUAGE'), 0, 2);
/*$this->config->get('translation.locales')*/
if ($locale != 'fr' && $locale != 'en') {
$locale = 'en';
}
Session::put('locale', $locale);
}
App::setLocale($locale);
//dd(Session::get('locale'));
Translation::setLocale($locale);
return $next($request);
}
}
<?php
namespace Stevebauman\Translation;
use ErrorException;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\App;
use InvalidArgumentException;
use Stevebauman\Translation\Contracts\Client as ClientInterface;
use Stevebauman\Translation\Contracts\Translation as TranslationInterface;
use UnexpectedValueException;
//use Illuminate\Http\Request;
class Translation implements TranslationInterface
{
/**
* Holds the application .
*
* #var object
*/
protected $app ;
/**
* Holds the application locale.
*
* #var string
*/
protected $locale = '';
/**
* Holds the locale model.
*
* #var Model
*/
protected $localeModel;
/**
* Holds the translation model.
*
* #var Model
*/
protected $translationModel;
/**
* Holds the translation client.
*
* #var ClientInterface
*/
protected $client;
/**
* Holds the current cache instance.
*
* #var \Illuminate\Cache\CacheManager
*/
protected $cache;
/**
* Holds the current config instance.
*
* #var \Illuminate\Config\Repository
*/
protected $config;
/**
* Holds the current request instance.
*
* #var \Illuminate\Http\Request
*/
protected $request;
/**
* The default amount of time (minutes) to store the cached translations.
*
* #var int
*/
private $cacheTime = 30;
/**
* {#inheritdoc}
*/
public function __construct(Application $app)
{
$this->app = $app;
$this->config = $this->app->make('config');
$this->cache = $this->app->make('cache');
$this->request = $this->app->make('request');
$this->localeModel = $this->app->make($this->getConfigLocaleModel());
$this->translationModel = $this->app->make($this->getConfigTranslationModel());
$this->client = $this->app->make($this->getConfigClient());
// Set the default locale to the current application locale
$this->setLocale($this->getConfigDefaultLocale());
// Set the cache time from the configuration
$this->setCacheTime($this->getConfigCacheTime());
}
/**
* {#inheritdoc}
*/
public function translate($text = '', $replacements = [], $toLocale = '', $runOnce = false)
{
try {
// Make sure $text is actually a string and not and object / int
$this->validateText($text);
// Get the default translation text. This will insert the translation
// and the default application locale if they don't
// exist using firstOrCreate
$defaultTranslation = $this->getDefaultTranslation($text);
// If there are replacements inside the array we need to convert them
// into google translate safe placeholders. ex :name to __name__
if (count($replacements) > 0) {
$defaultTranslation->translation = $this->makeTranslationSafePlaceholders($text, $replacements);
}
if ( Session::has('locale')) {
$locale = Session::get('locale');
} else {
$locale = substr($request->server('HTTP_ACCEPT_LANGUAGE'), 0, 2);
/*$this->config->get('translation.locales')*/
if ($locale != 'fr' && $locale != 'en') {
$locale = 'en';
}
Session::put('locale', $locale);
}
App::setLocale($locale);
//dd($locale);
Translation::setLocale($locale);
// If a toLocale has been provided, we're only translating a single string, so
// we won't call the getLocale method as it retrieves and sets the default
// session locale. If it has not been provided, we'll get the
// default locale, and set it on the current session.
if ($toLocale) {
$toLocale = $this->firstOrCreateLocale($toLocale);
} else {
//dd(Session::all());
$toLocale = $this->firstOrCreateLocale($this->getLocale());
}
// Check if translation is requested for default locale.
// If it is default locale we can just return default translation.
if ($defaultTranslation->getAttribute($this->localeModel->getForeignKey()) == $toLocale->getKey()) {
return $this->makeReplacements($defaultTranslation->translation, $replacements);
}
// Since we are not on default translation locale, we will have to
// create (or get first) translation for provided locale where
// parent translation is our default translation.
$translation = $this->firstOrCreateTranslation(
$toLocale,
$defaultTranslation->translation,
$defaultTranslation
);
//dd($this->makeReplacements($translation->translation, $replacements));
return $this->makeReplacements($translation->translation, $replacements);
} catch (\Illuminate\Database\QueryException $e) {
// If foreign key integrity constrains fail, we have a caching issue
if (!$runOnce) {
// If this has not been run before, proceed
// Burst locale cache
$this->removeCacheLocale($toLocale->code);
// Burst translation cache
$this->removeCacheTranslation($this->translationModel->firstOrNew([
$toLocale->getForeignKey() => $toLocale->getKey(),
'translation' => $text,
])
);
// Attempt translation 1 more time
return $this->translate($text, $replacements, $toLocale->code, $runOnce = true);
} else {
// If it has already tried translating once and failed again,
// prevent infinite loops and just return the text
return $text;
}
}
}
/**
* {#inheritdoc}
*/
public function getAppLocale()
{
return $this->config->get('app.locale');
}
/**
* {#inheritdoc}
*/
public function getRoutePrefix()
{
$locale = $this->request->segment($this->getConfigRequestSegment());
$locales = $this->getConfigLocales();
if (is_array($locales) && in_array($locale, array_keys($locales))) {
return $locale;
}
}
/**
* {#inheritdoc}
*/
public function getLocale()
{
//dd($this->request->session()->has('locale'));
/*if ($this->request->session()->has('locale')) {
return $this->request->session()->get('locale');
} else {
return $this->getConfigDefaultLocale();
}*/
/*if (session('locale')) {
return session('locale');
} else {
return $this->getConfigDefaultLocale();
}*/
return Session::get('locale'); //$this->locale;
}
/**
* {#inheritdoc}
*/
public function setLocale($code = '')
{
$this->locale = $code;
/*if(in_array($code, $this->config->get('translation.locales'))){*/
//$this->request->session()->put('locale', $locale);
session(['locale' => $this->locale]);
/* } else {} */
}
/**
* {#inheritdoc}
*/
public function getDefaultTranslation($text)
{
$locale = $this->firstOrCreateLocale($this->getConfigDefaultLocale());
return $this->firstOrCreateTranslation($locale, $text);
}
/**
* Replaces laravel translation placeholders with google
* translate safe placeholders. Ex:.
*
* Converts:
* :name
*
* Into:
* __name__
*
* #param string $text
* #param array $replace
*
* #return mixed
*/
protected function makeTranslationSafePlaceholders($text, array $replace = [])
{
if (count($replace) > 0) {
foreach ($replace as $key => $value) {
// Search for :key
$search = ':'.$key;
// Replace it with __key__
$replace = $this->makeTranslationSafePlaceholder($key);
// Perform the replacements
$text = str_replace($search, $replace, $text);
}
}
return $text;
}
/**
* Makes a placeholder by the specified key.
*
* #param string $key
*
* #return string
*/
protected function makeTranslationSafePlaceholder($key = '')
{
return '___'.strtolower($key).'___';
}
/**
* Make the place-holder replacements on the specified text.
*
* #param string $text
* #param array $replacements
*
* #return string
*/
protected function makeReplacements($text, array $replacements)
{
if (count($replacements) > 0) {
foreach ($replacements as $key => $value) {
$replace = $this->makeTranslationSafePlaceholder($key);
$text = str_ireplace($replace, $value, $text);
}
}
return $text;
}
/**
* Retrieves or creates a locale from the specified code.
*
* #param string $code
*
* #return Model
*/
protected function firstOrCreateLocale($code)
{
$cachedLocale = $this->getCacheLocale($code);
if ($cachedLocale) {
return $cachedLocale;
}
$name = $this->getConfigLocaleByCode($code);
$locale = $this->localeModel->firstOrCreate([
'code' => $code,
'name' => $name,
]);
$this->setCacheLocale($locale);
return $locale;
}
/**
* Creates a translation.
*
* #param Model $locale
* #param string $text
* #param Model $parentTranslation
*
* #return Model
*/
protected function firstOrCreateTranslation(Model $locale, $text, $parentTranslation = null)
{
// We'll check to see if there's a cached translation
// first before we try and hit the database.
$cachedTranslation = $this->getCacheTranslation($locale, $text);
if ($cachedTranslation instanceof Model) {
return $cachedTranslation;
}
// Check if auto translation is enabled. If so we'll run
// the text through google translate and
// save it, then cache it.
if ($parentTranslation && $this->autoTranslateEnabled()) {
$this->client->setSource($parentTranslation->locale->code);
$this->client->setTarget($locale->code);
try {
$text = $this->client->translate($text);
} catch (ErrorException $e) {
// Request to translate failed, set the text
// to the parent translation.
$text = $parentTranslation->translation;
} catch (UnexpectedValueException $e) {
// Looks like something other than text was passed in,
// we'll set the text to the parent translation
// for this exception as well.
$text = $parentTranslation->translation;
}
}
if ($parentTranslation) {
// If a parent translation is given we're looking for it's child translation.
$translation = $this->translationModel->firstOrNew([
$locale->getForeignKey() => $locale->getKey(),
$this->translationModel->getForeignKey() => $parentTranslation->getKey(),
]);
} else {
// Otherwise we're creating the parent translation.
$translation = $this->translationModel->firstOrNew([
$locale->getForeignKey() => $locale->getKey(),
'translation' => $text,
]);
}
if (empty($translation->getAttribute('translation'))) {
// We need to make sure we don't overwrite the translation
// if it exists already in case it was modified.
$translation->setAttribute('translation', $text);
}
if ($translation->isDirty()) {
$translation->save();
}
// Cache the translation so it's retrieved faster next time
$this->setCacheTranslation($translation);
return $translation;
}
/**
* Sets a cache key to the specified locale and text.
*
* #param Model $translation
*/
protected function setCacheTranslation(Model $translation)
{
if ($translation->parent instanceof Model) {
$id = $this->getTranslationCacheId($translation->locale, $translation->parent->translation);
} else {
$id = $this->getTranslationCacheId($translation->locale, $translation->translation);
}
if (!$this->cache->has($id)) {
$this->cache->put($id, $translation, $this->cacheTime);
}
}
/**
* Remove the translation from the cache manually.
*
* #param Model $translation
*/
protected function removeCacheTranslation(Model $translation)
{
$id = $this->getTranslationCacheId($translation->locale, $translation->translation);
if ($this->cache->has($id)) {
$this->cache->forget($id);
}
}
/**
* Retrieves the cached translation from the specified locale
* and text.
*
* #param Model $locale
* #param string $text
*
* #return bool|Model
*/
protected function getCacheTranslation(Model $locale, $text)
{
$id = $this->getTranslationCacheId($locale, $text);
$cachedTranslation = $this->cache->get($id);
if ($cachedTranslation instanceof Model) {
return $cachedTranslation;
}
// Cached translation wasn't found, let's return
// false so we know to generate one.
return false;
}
/**
* Sets a cache key to the specified locale.
*
* #param Model $locale
*/
protected function setCacheLocale(Model $locale)
{
if (!$this->cache->has($locale->code)) {
$id = sprintf('translation.%s', $locale->code);
$this->cache->put($id, $locale, $this->cacheTime);
}
}
/**
* Retrieves a cached locale from the specified locale code.
*
* #param string $code
*
* #return bool
*/
protected function getCacheLocale($code)
{
$id = sprintf('translation.%s', $code);
if ($this->cache->has($id)) {
return $this->cache->get($id);
}
return false;
}
/**
* Remove a locale from the cache.
*
* #param string $code
*/
protected function removeCacheLocale($code)
{
$id = sprintf('translation.%s', $code);
if ($this->cache->has($id)) {
$this->cache->forget($id);
}
}
/**
* Returns a unique translation code by compressing the text
* using a PHP compression function.
*
* #param Model $locale
* #param string $text
*
* #return string
*/
protected function getTranslationCacheId(Model $locale, $text)
{
$compressed = $this->compressString($text);
return sprintf('translation.%s.%s', $locale->code, $compressed);
}
/**
* Returns a the english name of the locale code entered from the config file.
*
* #param string $code
*
* #return string
*/
protected function getConfigLocaleByCode($code)
{
$locales = $this->getConfigLocales();
if (is_array($locales) && array_key_exists($code, $locales)) {
return $locales[$code];
}
return $code;
}
/**
* Sets the time to store the translations and locales in cache.
*
* #param int $time
*/
protected function setCacheTime($time)
{
if (is_numeric($time)) {
$this->cacheTime = $time;
}
}
/**
* Returns the default locale from the configuration.
*
* #return string
*/
protected function getConfigDefaultLocale()
{
return $this->config->get('translation.default_locale', 'en');
}
/**
* Returns the locale model from the configuration.
*
* #return string
*/
protected function getConfigLocaleModel()
{
return $this->config->get('translation.models.locale', Models\Locale::class);
}
/**
* Returns the translation model from the configuration.
*
* #return string
*/
protected function getConfigTranslationModel()
{
return $this->config->get('translation.models.translation', Models\Translation::class);
}
/**
* Returns the translation client from the configuration.
*
* #return string
*/
protected function getConfigClient()
{
return $this->config->get('translation.clients.client', Clients\GoogleTranslate::class);
}
/**
* Returns the request segment to retrieve the locale from.
*
* #return int
*/
protected function getConfigRequestSegment()
{
return $this->config->get('translation.request_segment', 1);
}
/**
* Returns the array of configuration locales.
*
* #return array
*/
protected function getConfigLocales()
{
return $this->config->get('translation.locales');
}
/**
* Returns the cache time set from the configuration file.
*
* #return string|int
*/
protected function getConfigCacheTime()
{
return $this->config->get('translation.cache_time', $this->cacheTime);
}
/**
* Returns the auto translate configuration option.
*
* #return bool
*/
protected function autoTranslateEnabled()
{
return $this->config->get('translation.auto_translate', true);
}
/**
* Calculates the md5 hash of a string.
*
* Used for storing cache keys for translations.
*
* #param $string
*
* #return string
*/
protected function compressString($string)
{
return md5($string);
}
/**
* Validates the inserted text to make sure it's a string.
*
* #param $text
*
* #throws InvalidArgumentException
*
* #return bool
*/
protected function validateText($text)
{
if (!is_string($text)) {
$message = 'Invalid Argument. You must supply a string to be translated.';
throw new InvalidArgumentException($message);
}
return true;
}
}
Thank you!!
I'm trying to "use" a vendor script to connect to feefo api (an online reviews service) but when I try and use the script it gives me this error:
Type error: Argument 1 passed to
BlueBayTravel\Feefo\Feefo::__construct() must be an instance of
GuzzleHttp\Client, null given, called in/Users/webuser1/Projects/_websites/domain.co.uk/plugins/gavinfoster/feefo/components/Feedback.php on line 47
Here is the vendor code I'm using:
/*
* This file is part of Feefo.
*
* (c) Blue Bay Travel <developers#bluebaytravel.co.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace BlueBayTravel\Feefo;
use ArrayAccess;
use Countable;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Support\Arrayable;
use SimpleXMLElement;
/**
* This is the feefo class.
*
* #author James Brooks <james#bluebaytravel.co.uk>
*/
class Feefo implements Arrayable, ArrayAccess, Countable
{
/**
* The guzzle client.
*
* #var \GuzzleHttp\Client
*/
protected $client;
/**
* The config repository.
*
* #var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* The review items.
*
* #var array
*/
protected $data;
/**
* Create a new feefo instance.
*
* #param \GuzzleHttp\Client $client
* #param \Illuminate\Contracts\Config\Repository $config
*
* #return void
*/
public function __construct(Client $client, Repository $config)
{
$this->client = $client;
$this->config = $config;
}
/**
* Fetch feedback.
*
* #param array|null $params
*
* #return \BlueBayTravel\Feefo\Feefo
*/
public function fetch($params = null)
{
if ($params === null) {
$params['json'] = true;
$params['mode'] = 'both';
}
$params['logon'] = $this->config->get('feefo.logon');
$params['password'] = $this->config->get('feefo.password');
try {
$body = $this->client->get($this->getRequestUrl($params));
return $this->parse((string) $body->getBody());
} catch (Exception $e) {
throw $e; // Re-throw the exception
}
}
/**
* Parses the response.
*
* #param string $data
*
* #return \Illuminate\Support\Collection
*/
protected function parse($data)
{
$xml = new SimpleXMLElement($data);
foreach ((array) $xml as $items) {
if (isset($items->TOTALRESPONSES)) {
continue;
}
foreach ($items as $item) {
$this->data[] = new FeefoItem((array) $item);
}
}
return $this;
}
/**
* Assigns a value to the specified offset.
*
* #param mixed $offset
* #param mixed $value
*
* #return void
*/
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->data[] = $value;
} else {
$this->data[$offset] = $value;
}
}
/**
* Whether or not an offset exists.
*
* #param mixed $offset
*
* #return bool
*/
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
/**
* Unsets an offset.
*
* #param mixed $offset
*
* #return void
*/
public function offsetUnset($offset)
{
if ($this->offsetExists($offset)) {
unset($this->data[$offset]);
}
}
/**
* Returns the value at specified offset.
*
* #param mixed $offset
*
* #return mixed
*/
public function offsetGet($offset)
{
return $this->offsetExists($offset) ? $this->data[$offset] : null;
}
/**
* Count the number of items in the dataset.
*
* #return int
*/
public function count()
{
return count($this->data);
}
/**
* Get the instance as an array.
*
* #return array
*/
public function toArray()
{
return $this->data;
}
/**
* Returns the Feefo API endpoint.
*
* #param array $params
*
* #return string
*/
protected function getRequestUrl(array $params)
{
$query = http_build_query($params);
return sprintf('%s?%s', $this->config->get('feefo.baseuri'), $query);
}
}
And here is the code I'm using to try and use the fetch() method from the vendor class:
use Cms\Classes\ComponentBase;
use ArrayAccess;
use Countable;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Support\Arrayable;
use SimpleXMLElement;
use BlueBayTravel\Feefo\Feefo;
class Feedback extends ComponentBase
{
public $client;
public $config;
/**
* Container used for display
* #var BlueBayTravel\Feefo
*/
public $feedback;
public function componentDetails()
{
return [
'name' => 'Feedback Component',
'description' => 'Adds Feefo feedback to the website'
];
}
public function defineProperties()
{
return [];
}
public function onRun()
{
$this->feedback = $this->page['feedback'] = $this->loadFeedback($this->client, $this->config);
}
public function loadFeedback($client, $config)
{
$feefo = new Feefo($client, $config);
$feedback = $feefo->fetch();
return $feedback;
}
}
Won't allow me to call the Fetch() method statically so trying to instantiate and then use:
public function loadFeedback($client, $config)
{
$feefo = new Feefo($client, $config);
$feedback = $feefo->fetch();
return $feedback;
}
I've also tried type hinting the args like this:
public function loadFeedback(Client $client, Repository $config)
{
$feefo = new Feefo($client, $config);
$feedback = $feefo->fetch();
return $feedback;
}
But still I get the exception error above. I'm struggling to understand how to get past this. Any help for a newbie much appreciated :)
Just type hinting the function won't cast it to that object type. You need to properly pass the Guzzle\Client object to your function call.
// Make sure you 'use' the GuzzleClient on top of the class
// or use the Fully Qualified Class Name of the Client
$client = new Client();
$feedback = new Feedback();
// Now we passed the Client object to the function of the feedback class
// which will lead to the constructor of the Feefo class which is
// where your error is coming from.
$loadedFeedback = $feedback->loadFeedback($client);
Don't forget to do the same for the Repository $config from Laravel/Lumen
I've setup my server with Yii2 and Humhub ext,
What i'm trying to do is set a rest api to it , but I get 500 Internal server error :
"message": "Cannot declare class humhub\modules\content\models\Content, because the name is already in use",
"file": "***\content\models\Content.php",
The first class is :
<?php
namespace app\models;
use Yii;
include '../humhub/modules/content/models/content.php';
class Content extends \humhub\modules\content\models\Content
{
public function getPosts()
{
return $this->hasMany(Post::className(), ['id' => 'object_id']);
}
}
The second class is (pretty big):
<?php
/**
* #link https://www.humhub.org/
* #copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* #license https://www.humhub.com/licences
*/
namespace humhub\modules\content\models;
include __DIR__ .'/../../../modules\content\models\ContentDeprecated.php';
use Yii;
use humhub\modules\user\components\PermissionManager;
use yii\base\Exception;
use yii\helpers\Url;
use humhub\modules\user\models\User;
use humhub\modules\space\models\Space;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\content\components\ContentContainerActiveRecord;
use humhub\modules\content\permissions\ManageContent;
use yii\rbac\Permission;
/**
* This is the model class for table "content".
*
*
* The followings are the available columns in table 'content':
* #property integer $id
* #property string $guid
* #property string $object_model
* #property integer $object_id
* #property integer $visibility
* #property integer $pinned
* #property string $archived
* #property string $created_at
* #property integer $created_by
* #property string $updated_at
* #property integer $updated_by
* #property ContentContainer $contentContainer
*
* #since 0.5
*/
class Content extends ContentDeprecated
{
/**
* A array of user objects which should informed about this new content.
*
* #var array User
*/
public $notifyUsersOfNewContent = [];
/**
* #var int The private visibility mode (e.g. for space member content or user profile posts for friends)
*/
const VISIBILITY_PRIVATE = 0;
/**
* #var int Public visibility mode, e.g. content which are visibile for followers
*/
const VISIBILITY_PUBLIC = 1;
/**
* #var int Owner visibility mode, only visible for contentContainer + content owner
*/
const VISIBILITY_OWNER = 2;
/**
* #var ContentContainerActiveRecord the Container (e.g. Space or User) where this content belongs to.
*/
protected $_container = null;
/**
* #inheritdoc
*/
public function behaviors()
{
return [
[
'class' => \humhub\components\behaviors\PolymorphicRelation::className(),
'mustBeInstanceOf' => array(ContentActiveRecord::className()),
],
[
'class' => \humhub\components\behaviors\GUID::className(),
],
];
}
/**
* #inheritdoc
*/
public static function tableName()
{
return 'content';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['object_id', 'visibility', 'pinned'], 'integer'],
[['archived'], 'safe'],
[['guid'], 'string', 'max' => 45],
[['object_model'], 'string', 'max' => 100],
[['object_model', 'object_id'], 'unique', 'targetAttribute' => ['object_model', 'object_id'], 'message' => 'The combination of Object Model and Object ID has already been taken.'],
[['guid'], 'unique']
];
}
/**
* Returns a Content Object by given Class and ID
*
* #param string $className Class Name of the Content
* #param int $id Primary Key
*/
public static function Get($className, $id)
{
$content = self::findOne(['object_model' => $className, 'object_id' => $id]);
if ($content != null) {
return $className::findOne(['id' => $id]);
}
return null;
}
/**
* #inheritdoc
*/
public function beforeSave($insert)
{
if ($this->object_model == "" || $this->object_id == "") {
throw new Exception("Could not save content with object_model or object_id!");
}
// Set some default values
if (!$this->archived) {
$this->archived = 0;
}
if (!$this->visibility) {
$this->visibility = self::VISIBILITY_PRIVATE;
}
if (!$this->pinned) {
$this->pinned = 0;
}
if ($insert) {
if ($this->created_by == "") {
$this->created_by = Yii::$app->user->id;
}
}
$this->stream_sort_date = new \yii\db\Expression('NOW()');
if ($this->created_by == "") {
throw new Exception("Could not save content without created_by!");
}
return parent::beforeSave($insert);
}
/**
* #inheritdoc
*/
public function afterSave($insert, $changedAttributes)
{
$contentSource = $this->getPolymorphicRelation();
foreach ($this->notifyUsersOfNewContent as $user) {
$contentSource->follow($user->id);
}
if ($insert && !$contentSource instanceof \humhub\modules\activity\models\Activity) {
if ($this->container !== null) {
$notifyUsers = array_merge($this->notifyUsersOfNewContent, Yii::$app->notification->getFollowers($this));
\humhub\modules\content\notifications\ContentCreated::instance()
->from($this->user)
->about($contentSource)
->sendBulk($notifyUsers);
\humhub\modules\content\activities\ContentCreated::instance()
->about($contentSource)->save();
Yii::$app->live->send(new \humhub\modules\content\live\NewContent([
'sguid' => ($this->container instanceof Space) ? $this->container->guid : null,
'uguid' => ($this->container instanceof User) ? $this->container->guid : null,
'originator' => $this->user->guid,
'contentContainerId' => $this->container->contentContainerRecord->id,
'visibility' => $this->visibility,
'contentId' => $this->id
]));
}
}
return parent::afterSave($insert, $changedAttributes);
}
/**
* #inheritdoc
*/
public function afterDelete()
{
// Try delete the underlying object (Post, Question, Task, ...)
$this->resetPolymorphicRelation();
if ($this->getPolymorphicRelation() !== null) {
$this->getPolymorphicRelation()->delete();
}
parent::afterDelete();
}
/**
* Returns the visibility of the content object
*
* #return Integer
*/
public function getVisibility()
{
return $this->visibility;
}
/**
* Checks if the content visiblity is set to public.
*
* #return boolean
*/
public function isPublic()
{
return $this->visibility == self::VISIBILITY_PUBLIC;
}
/**
* Checks if the content visiblity is set to private.
*
* #return boolean
*/
public function isPrivate()
{
return $this->visibility == self::VISIBILITY_PRIVATE;
}
/**
* Checks if the content object is pinned
*
* #return Boolean
*/
public function isPinned()
{
return ($this->pinned);
}
/**
* Pins the content object
*/
public function pin()
{
$this->pinned = 1;
//This prevents the call of beforesave, and the setting of update_at
$this->updateAttributes(['pinned']);
}
/**
* Unpins the content object
*/
public function unpin()
{
$this->pinned = 0;
$this->updateAttributes(['pinned']);
}
/**
* Checks if the user can pin this content.
* This is only allowed for workspace owner.
*
* #return boolean
*/
public function canPin()
{
if ($this->isArchived()) {
return false;
}
return $this->getContainer()->permissionManager->can(new ManageContent());
}
/**
* Creates a list of pinned content objects of the wall
*
* #return Int
*/
public function countPinnedItems()
{
return Content::find()->where(['content.contentcontainer_id' => $this->contentcontainer_id, 'content.pinned' => 1])->count();
}
/**
* Checks if current content object is archived
*
* #return boolean
*/
public function isArchived()
{
return $this->archived || ($this->getContainer() !== null && $this->getContainer()->isArchived());
}
/**
* Checks if the current user can archive this content.
* The content owner and the workspace admin can archive contents.
*
* #return boolean
*/
public function canArchive()
{
// Disabled on user profiles, there is no stream filter available yet.
if ($this->getContainer() instanceof User) {
return false;
}
return $this->getContainer()->permissionManager->can(new ManageContent());
}
/**
* Archives the content object
*/
public function archive()
{
if ($this->canArchive()) {
if ($this->isPinned()) {
$this->unpin();
}
$this->archived = 1;
if (!$this->save()) {
throw new Exception("Could not archive content!" . print_r($this->getErrors(), 1));
}
}
}
/**
* Unarchives the content object
*/
public function unarchive()
{
if ($this->canArchive()) {
$this->archived = 0;
$this->save();
}
}
/**
* Returns the url of this content.
*
* By default is returns the url of the wall entry.
*
* Optionally it's possible to create an own getUrl method in the underlying
* HActiveRecordContent (e.g. Post) to overwrite this behavior.
* e.g. in case there is no wall entry available for this content.
*
* #since 0.11.1
*/
public function getUrl()
{
if (method_exists($this->getPolymorphicRelation(), 'getUrl')) {
return $this->getPolymorphicRelation()->getUrl();
}
return Url::toRoute(['/content/perma', 'id' => $this->id]);
}
/**
* Sets container (e.g. space or user record) for this content.
*
* #param ContentContainerActiveRecord $container
* #throws Exception
*/
public function setContainer(ContentContainerActiveRecord $container)
{
$this->contentcontainer_id = $container->contentContainerRecord->id;
$this->_container = $container;
}
/**
* Returns the content container (e.g. space or user record) of this content
*
* #return ContentContainerActiveRecord
* #throws Exception
*/
public function getContainer()
{
if ($this->_container != null) {
return $this->_container;
}
if ($this->contentContainer !== null) {
$this->_container = $this->contentContainer->getPolymorphicRelation();
}
return $this->_container;
}
/**
* Relation to ContentContainer model
* Note: this is not a Space or User instance!
*
* #since 1.1
* #return \yii\db\ActiveQuery
*/
public function getContentContainer()
{
return $this->hasOne(ContentContainer::className(), ['id' => 'contentcontainer_id']);
}
/**
* Checks if the given user can edit this content.
*
* A user can edit a content if one of the following conditions are met:
*
* - User is the owner of the content
* - User is system administrator and the content module setting `adminCanEditAllContent` is set to true (default)
* - The user is granted the managePermission set by the model record class
* - The user meets the additional condition implemented by the model records class own `canEdit()` function.
*
* #since 1.1
* #param User $user
* #return bool can edit this content
*/
public function canEdit($user = null)
{
if (Yii::$app->user->isGuest) {
return false;
}
if ($user === null) {
$user = Yii::$app->user->getIdentity();
}
// Only owner can edit his content
if ($user !== null && $this->created_by == $user->id) {
return true;
}
// Global Admin can edit/delete arbitrarily content
if (Yii::$app->getModule('content')->adminCanEditAllContent && $user->isSystemAdmin()) {
return true;
}
/* #var $model ContentActiveRecord */
$model = $this->getPolymorphicRelation();
// Check additional manage permission for the given container
if ($model->hasManagePermission() && $this->getContainer() && $this->getContainer()->getPermissionManager($user)->can($model->getManagePermission())) {
return true;
}
// Check if underlying models canEdit implementation
// ToDo: Implement this as interface
if (method_exists($model, 'canEdit') && $model->canEdit($user)) {
return true;
}
return false;
}
/**
* Checks the given $permission of the current user in the contents content container.
* This is short for `$this->getContainer()->getPermissionManager()->can()`.
*
* #param $permission
* #param array $params
* #param bool $allowCaching
* #see PermissionManager::can()
* #since 1.2.1
* #return bool
*/
public function can($permission, $params = [], $allowCaching = true)
{
return $this->getContainer()->getPermissionManager()->can($permission, $params, $allowCaching);
}
/**
* Checks if user can view this content.
*
* #since 1.1
* #param User $user
* #return boolean can view this content
*/
public function canView($user = null)
{
if (!$user && !Yii::$app->user->isGuest) {
$user = Yii::$app->user->getIdentity();
}
// Check Guest Visibility
if (!$user) {
return $this->checkGuestAccess();
}
// Public visible content
if ($this->isPublic()) {
return true;
}
// Check system admin can see all content module configuration
if ($user->isSystemAdmin() && Yii::$app->getModule('content')->adminCanViewAllContent) {
return true;
}
if ($this->isPrivate() && $this->getContainer()->canAccessPrivateContent($user)) {
return true;
}
return false;
}
/**
* Determines if a guest user is able to read this content.
* This is the case if all of the following conditions are met:
*
* - The content is public
* - The `auth.allowGuestAccess` module setting is enabled
* - The space or profile visibility is set to VISIBILITY_ALL
*
* #return bool
*/
public function checkGuestAccess()
{
if(!$this->isPublic() || !Yii::$app->getModule('user')->settings->get('auth.allowGuestAccess')) {
return false;
}
// Check container visibility for guests
return ($this->container instanceof Space && $this->container->visibility == Space::VISIBILITY_ALL)
|| ($this->container instanceof User && $this->container->visibility == User::VISIBILITY_ALL);
}
/**
* Updates the wall/stream sorting time of this content for "updated at" sorting
*/
public function updateStreamSortTime()
{
$this->updateAttributes(['stream_sort_date' => new \yii\db\Expression('NOW()')]);
}
}
As you can see the first class extends the second one , I'm rather new to php but i'm guessing that's the issue
UPDATE : I tried using the "use class as " and still the same error , I also tried changing the name of the first class to Content1 just to check and still the same .
The problem occours in the second class from the error report
This is a recurring error in a few places in the server I figure I will solve it here and the rest will be the same ,
Many Thanks!
You included file with exacly same class name, so you have 2 class declarations with name Content in this file. Easiest ways to fix it:
Change one of class name
Start using Yii2 autloader, if you need to include something - there's something wrong in your code. You should be able to access
class only by using namespace. There's small conditions you have to
met:
Each class must be under a namespace (e.g. foo\bar\MyClass)
Each class must be saved in an individual file whose path is determined by the following algorithm:
// $className is a fully qualified class name without the leading backslash
$classFile = Yii::getAlias('#' . str_replace('\\', '/', $className) . '.php');
More about Yii2 autloading: Class Autoloading
I am trying to insert data into db ,but it shows some error like this
My model Entity
Request.php
is here `<?php
namespace EvolisClientRequest\Model\Entities;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Request
{
/**
* #var \Ramsey\Uuid\Uuid
* #ORM\Id
* #ORM\Column(type="uuid")
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="Salesperson", inversedBy="request")
* #ORM\JoinTable(name="request_salesperson")
* #var Salesperson
*/
private $salesperson;
/**
* #ORM\ManyToOne(targetEntity="Client", inversedBy="request")
* #var Client
*/
private $client;
/**
* #ORM\ManyToMany(targetEntity="Status", inversedBy="request")
* #ORM\JoinTable(name="request_status")
* #var Status
*/
private $status;
/**
* #ORM\Column(type="integer")
* #var Qualification
*/
private $qualification;
/**
* #return \Ramsey\Uuid\Uuid
*/
public function getId()
{
return $this->id;
}
/**
* #return Salesperson
*/
public function getSalesperson()
{
return $this->salesperson;
}
/**
* #param Salesperson $salesperson
*/
public function setSalesperson($salesperson)
{
$this->salesperson = $salesperson;
}
/**
* #return Client
*/
public function getClient()
{
return $this->client;
}
/**
* #param Client $client
*/
public function setClient($client)
{
$this->client = $client;
}
/**
* #return Status
*/
public function getStatus()
{
return $this->status;
}
/**
* #param Status $status
*/
public function setStatus($status)
{
$this->status = $status;
}
/**
* #return Qualification
*/
public function getQualification()
{
return $this->qualification;
}
/**
* #param Qualification $qualification
*/
public function setQualification($qualification)
{
$this->qualification = $qualification;
}
public function __construct($salesperson, $client, $status, $qualification) {
$this->salesperson = $salesperson;
$this->client = $client;
$this->status = $status;
$this->qualification = $qualification;
}
}`
Also my
DAO "RequestBaseDao.php" is here,which is automatically generated.
<?php
/*
* This file has been automatically generated by Mouf/ORM.
* DO NOT edit this file, as it might be overwritten.
* If you need to perform changes, edit the RequestDao class instead!
*/
namespace EvolisClientRequest\Model\DAOs;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NonUniqueResultException;
use Mouf\Doctrine\ORM\Event\SaveListenerInterface;
use EvolisClientRequest\Model\Entities\Request;
/**
* The RequestBaseDao class will maintain the persistence of Request class into the request table.
*
* #method Request findByQualification($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneByQualification($fieldValue, $orderBy = null)
* #method Request findBySurfaceMin($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneBySurfaceMin($fieldValue, $orderBy = null)
* #method Request findBySurfaceMax($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneBySurfaceMax($fieldValue, $orderBy = null)
* #method Request findByPriceMin($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneByPriceMin($fieldValue, $orderBy = null)
* #method Request findByPriceMax($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneByPriceMax($fieldValue, $orderBy = null)
* #method Request findByRequestDate($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneByRequestDate($fieldValue, $orderBy = null)
*/
class RequestBaseDao extends EntityRepository
{
/**
* #var SaveListenerInterface[]
*/
private $saveListenerCollection;
/**
* #param EntityManagerInterface $entityManager
* #param SaveListenerInterface[] $saveListenerCollection
*/
public function __construct(EntityManagerInterface $entityManager, array $saveListenerCollection = [])
{
parent::__construct($entityManager, $entityManager->getClassMetadata('EvolisClientRequest\Model\Entities\Request'));
$this->saveListenerCollection = $saveListenerCollection;
}
/**
* Get a new persistent entity
* #param ...$params
* #return Request
*/
public function create(...$params) : Request
{
$entity = new Request(...$params);
$this->getEntityManager()->persist($entity);
return $entity;
}
/**
* Peforms a flush on the entity.
*
* #param Request
* #throws \Exception
*/
public function save(Request $entity)
{
foreach ($this->saveListenerCollection as $saveListener) {
$saveListener->preSave($entity);
}
$this->getEntityManager()->flush($entity);
foreach ($this->saveListenerCollection as $saveListener) {
$saveListener->postSave($entity);
}
}
/**
* Peforms remove on the entity.
*
* #param Request $entity
*/
public function remove(Request $entity)
{
$this->getEntityManager()->remove($entity);
}
/**
* Finds only one entity. The criteria must contain all the elements needed to find a unique entity.
* Throw an exception if more than one entity was found.
*
* #param array $criteria
*
* #return Request
*/
public function findUniqueBy(array $criteria) : Request
{
$result = $this->findBy($criteria);
if (count($result) === 1) {
return $result[0];
} elseif (count($result) > 1) {
throw new NonUniqueResultException('More than one Request was found');
} else {
return;
}
}
/**
* Finds only one entity by Qualification.
* Throw an exception if more than one entity was found.
*
* #param mixed $fieldValue the value of the filtered field
*
* #return Request
*/
public function findUniqueByQualification($fieldValue)
{
return $this->findUniqueBy(array('qualification' => $fieldValue));
}
}
My RequestDao.php where i can write queries.
<?php
namespace EvolisClientRequest\Model\DAOs;
use EvolisClientRequest\Model\Entities\Request;
/**
* The RequestDao class will maintain the persistence of Request class into the request table.
*/
class RequestDao extends RequestBaseDao {
/*** PUT YOUR SPECIFIC QUERIES HERE !! ***/
public function setdata()
{
/*$product = new Request();
$product->setStatus('Keyboard');
$product->setClient('000000001ae10dda000000003c4667a6');
$product->setSalesperson('Ergonomic and stylish!');
$product->setQualification('1111');
//var_dump($r);die();
$em = $this->getEntityManager();
$em->persist($product);
$em->flush();*/
$product= $this->create('Keyboard','000000001ae10dda000000003c4667a6','Ergonomic and stylish!','1111');
$this->save($product);
}
}
Finally my Controller "ContactController.php"
<?php
namespace EvolisClientRequest\Controllers;
use EvolisClientRequest\Model\DAOs\ClientDao;
use EvolisClientRequest\Model\Entities\Client;
use EvolisClientRequest\Model\Entities\Clients;
use EvolisClientRequest\Model\DAOs\RequestDao;
use EvolisClientRequest\Model\Entities\Request;
use EvolisClientRequest\Model\Entities\Requests;
use EvolisClientRequest\Model\DAOs\SalespersonDao;
use EvolisClientRequest\Model\Entities\Salesperson;
use EvolisClientRequest\Model\Entities\Salespersons;
use Mouf\Mvc\Splash\Annotations\Get;
use Mouf\Mvc\Splash\Annotations\Post;
use Mouf\Mvc\Splash\Annotations\Put;
use Mouf\Mvc\Splash\Annotations\Delete;
use Mouf\Mvc\Splash\Annotations\URL;
use Mouf\Html\Template\TemplateInterface;
use Mouf\Html\HtmlElement\HtmlBlock;
use Psr\Log\LoggerInterface;
use \Twig_Environment;
use Mouf\Html\Renderer\Twig\TwigTemplate;
use Mouf\Mvc\Splash\HtmlResponse;
use Doctrine\DBAL\DriverManager;
use Zend\Diactoros\Response\JsonResponse;
use Doctrine\Common\Collections\ArrayCollection;
/**
* TODO: write controller comment
*/
class ContactController {
/**
* The logger used by this controller.
* #var LoggerInterface
*/
private $logger;
/**
* The template used by this controller.
* #var TemplateInterface
*/
private $template;
/**
* The header of the page.
* #var HtmlBlock
*/
private $header;
/**
* The main content block of the page.
* #var HtmlBlock
*/
private $content;
/**
* The Twig environment (used to render Twig templates).
* #var Twig_Environment
*/
private $twig;
/**
* Controller's constructor.
* #param LoggerInterface $logger The logger
* #param TemplateInterface $template The template used by this controller
* #param HtmlBlock $content The main content block of the page
* #param Twig_Environment $twig The Twig environment (used to render Twig templates)
*/
public function __construct(LoggerInterface $logger, TemplateInterface $template, HtmlBlock $content, HtmlBlock $header, Twig_Environment $twig, ClientDao $clientDao, RequestDao $requestDao, SalespersonDao $salespersonDao) {
$this->logger = $logger;
$this->template = $template;
$this->content = $content;
$this->twig = $twig;
$this->header = $header;
$this->clientDao = $clientDao;
$this->requestDao = $requestDao;
$this->salespersonDao = $salespersonDao;
}
/**
* #URL("new.html")
*/
public function new() {
// TODO: write content of action here
// Let's add the twig file to the template.
$this->content->addHtmlElement(new TwigTemplate($this->twig, 'views/contact/new.twig', array("message"=>"world")));
$this->header->addHtmlElement(new TwigTemplate($this->twig, 'views/root/header.twig', array("message"=>"world")));
return new HtmlResponse($this->template);
}
/**
* #URL("saveData")
* For Saving the data
*/
public function saveData()
{
/*$newClient = $this->clientDao->create('hello', 'sarathchandran#122.com','8907263949');
$this->clientDao->save($newClient);*/
//$data = array();
//$data['salespersonDao']['salesperson'] = 'example#sales.com';
//$data['request']['qualification'] = 'abcdefgh';
//$newClient = $this->requestDao->create($data);
//$newClient = $this->requestDao->setQualification('Keyboard');
// $this->requestDao->save($newClient);
$user_data=$this->requestDao->setdata();
//return new JsonResponse([ "status"=>0 ]);
}
}
I am using Mouf framework.I am stuck with this problem.Someone Please help me to solve this problem.
Thanks in advance
As advised by #rokas, you should really start reading more about Doctrine. This is not a Mouf issue, this is clearly a Doctrine ORM issue so the appropriate doc is here: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html
Here are a few tips:
Your controller calls the setdata method
$user_data=$this->requestDao->setdata();
The setdata method calls:
$product= $this->create('Keyboard','000000001ae10dda000000003c4667a6','Ergonomic and stylish!','1111');
Now, the create method is:
public function create(...$params) : Request
{
$entity = new Request(...$params);
$this->getEntityManager()->persist($entity);
return $entity;
}
This means that it will call the Request constructor with this parameters:
$entity = new Request('Keyboard','000000001ae10dda000000003c4667a6','Ergonomic and stylish!','1111');
Have a look at the Request constructor:
public function __construct($salesperson, $client, $status, $qualification) {
$this->salesperson = $salesperson;
$this->client = $client;
$this->status = $status;
$this->qualification = $qualification;
}
As you can see, the first parameter is $salesperson. You try to put the value "Keyboard" here. The $salesperson attribute is defined this way:
/**
* #ORM\ManyToMany(targetEntity="Salesperson", inversedBy="request")
* #ORM\JoinTable(name="request_salesperson")
* #var Salesperson
*/
private $salesperson;
/**
* #param Salesperson $salesperson
*/
public function setSalesperson($salesperson)
{
$this->salesperson = $salesperson;
}
Now, here is your problem I think.
The $salesperson property is defined as a "ManyToMany" association. So you really cannot put a string in here, it is a collection of Salesperson. By the way, you should not "set" anything either. The setter should be completely removed.
Instead, you should consider using it as per the documentation here: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html
For instance:
$request->getSalesPersons()->add($someObjectRepresentingAPerson);
Also, notice that since the $salesperson represents a collection of Salesperson object, your should really name it $salesPersons (plural form)
I really need to be able to extract the metadata from a .ttf true type font file.
I'm building a central database of all the fonts all our designers use (they're forever swapping fonts via email to take over design elements, etc). I want to get all the fonts, some have silly names like 00001.ttf, so file name is no help, but I know the fonts have metadata, I need some way to extract that in PHP.
Then I can create a loop to look through the directories I've specified, get this data (and any other data I can get at the same time, and add it to a database.
I just really need help with the reading of this metadata part.
I came across this link. It will do what you want (I've tested it and posted results). Just pass the class the path of the TTF file you want to parse the data out of. then use $fontinfo[1].' '.$fontinfo[2] for the name.
In case you don't want to register, here is the class
Resulting Data
Array
(
[1] => Almonte Snow
[2] => Regular
[3] => RayLarabie: Almonte Snow: 2000
[4] => Almonte Snow
[5] => Version 2.000 2004
[6] => AlmonteSnow
[8] => Ray Larabie
[9] => Ray Larabie
[10] => Larabie Fonts is able to offer unique free fonts through the generous support of visitors to the site. Making fonts is my full-time job and every donation, in any amount, enables me to continue running the site and creating new fonts. If you would like to support Larabie Fonts visit www.larabiefonts.com for details.
[11] => http://www.larabiefonts.com
[12] => http://www.typodermic.com
)
Usage
<?php
include 'ttfInfo.class.php';
$fontinfo = getFontInfo('c:\windows\fonts\_LDS_almosnow.ttf');
echo '<pre>';
print_r($fontinfo);
echo '</pre>';
?>
ttfInfo.class.php
<?php
/**
* ttfInfo class
* Retrieve data stored in a TTF files 'name' table
*
* #original author Unknown
* found at http://www.phpclasses.org/browse/package/2144.html
*
* #ported for used on http://www.nufont.com
* #author Jason Arencibia
* #version 0.2
* #copyright (c) 2006 GrayTap Media
* #website http://www.graytap.com
* #license GPL 2.0
* #access public
*
* #todo: Make it Retrieve additional information from other tables
*
*/
class ttfInfo {
/**
* variable $_dirRestriction
* Restrict the resource pointer to this directory and above.
* Change to 1 for to allow the class to look outside of it current directory
* #protected
* #var int
*/
protected $_dirRestriction = 1;
/**
* variable $_dirRestriction
* Restrict the resource pointer to this directory and above.
* Change to 1 for nested directories
* #protected
* #var int
*/
protected $_recursive = 0;
/**
* variable $fontsdir
* This is to declare this variable as protected
* don't edit this!!!
* #protected
*/
protected $fontsdir;
/**
* variable $filename
* This is to declare this varable as protected
* don't edit this!!!
* #protected
*/
protected $filename;
/**
* function setFontFile()
* set the filename
* #public
* #param string $data the new value
* #return object reference to this
*/
public function setFontFile($data)
{
if ($this->_dirRestriction && preg_match('[\.\/|\.\.\/]', $data))
{
$this->exitClass('Error: Directory restriction is enforced!');
}
$this->filename = $data;
return $this;
} // public function setFontFile
/**
* function setFontsDir()
* set the Font Directory
* #public
* #param string $data the new value
* #return object referrence to this
*/
public function setFontsDir($data)
{
if ($this->_dirRestriction && preg_match('[\.\/|\.\.\/]', $data))
{
$this->exitClass('Error: Directory restriction is enforced!');
}
$this->fontsdir = $data;
return $this;
} // public function setFontsDir
/**
* function readFontsDir()
* #public
* #return information contained in the TTF 'name' table of all fonts in a directory.
*/
public function readFontsDir()
{
if (empty($this->fontsdir)) { $this->exitClass('Error: Fonts Directory has not been set with setFontsDir().'); }
if (empty($this->backupDir)){ $this->backupDir = $this->fontsdir; }
$this->array = array();
$d = dir($this->fontsdir);
while (false !== ($e = $d->read()))
{
if($e != '.' && $e != '..')
{
$e = $this->fontsdir . $e;
if($this->_recursive && is_dir($e))
{
$this->setFontsDir($e);
$this->array = array_merge($this->array, readFontsDir());
}
else if ($this->is_ttf($e) === true)
{
$this->setFontFile($e);
$this->array[$e] = $this->getFontInfo();
}
}
}
if (!empty($this->backupDir)){ $this->fontsdir = $this->backupDir; }
$d->close();
return $this;
} // public function readFontsDir
/**
* function setProtectedVar()
* #public
* #param string $var the new variable
* #param string $data the new value
* #return object reference to this
* DISABLED, NO REAL USE YET
public function setProtectedVar($var, $data)
{
if ($var == 'filename')
{
$this->setFontFile($data);
} else {
//if (isset($var) && !empty($data))
$this->$var = $data;
}
return $this;
}
*/
/**
* function getFontInfo()
* #public
* #return information contained in the TTF 'name' table.
*/
public function getFontInfo()
{
$fd = fopen ($this->filename, "r");
$this->text = fread ($fd, filesize($this->filename));
fclose ($fd);
$number_of_tables = hexdec($this->dec2ord($this->text[4]).$this->dec2ord($this->text[5]));
for ($i=0;$i<$number_of_tables;$i++)
{
$tag = $this->text[12+$i*16].$this->text[12+$i*16+1].$this->text[12+$i*16+2].$this->text[12+$i*16+3];
if ($tag == 'name')
{
$this->ntOffset = hexdec(
$this->dec2ord($this->text[12+$i*16+8]).$this->dec2ord($this->text[12+$i*16+8+1]).
$this->dec2ord($this->text[12+$i*16+8+2]).$this->dec2ord($this->text[12+$i*16+8+3]));
$offset_storage_dec = hexdec($this->dec2ord($this->text[$this->ntOffset+4]).$this->dec2ord($this->text[$this->ntOffset+5]));
$number_name_records_dec = hexdec($this->dec2ord($this->text[$this->ntOffset+2]).$this->dec2ord($this->text[$this->ntOffset+3]));
}
}
$storage_dec = $offset_storage_dec + $this->ntOffset;
$storage_hex = strtoupper(dechex($storage_dec));
for ($j=0;$j<$number_name_records_dec;$j++)
{
$platform_id_dec = hexdec($this->dec2ord($this->text[$this->ntOffset+6+$j*12+0]).$this->dec2ord($this->text[$this->ntOffset+6+$j*12+1]));
$name_id_dec = hexdec($this->dec2ord($this->text[$this->ntOffset+6+$j*12+6]).$this->dec2ord($this->text[$this->ntOffset+6+$j*12+7]));
$string_length_dec = hexdec($this->dec2ord($this->text[$this->ntOffset+6+$j*12+8]).$this->dec2ord($this->text[$this->ntOffset+6+$j*12+9]));
$string_offset_dec = hexdec($this->dec2ord($this->text[$this->ntOffset+6+$j*12+10]).$this->dec2ord($this->text[$this->ntOffset+6+$j*12+11]));
if (!empty($name_id_dec) and empty($font_tags[$name_id_dec]))
{
for($l=0;$l<$string_length_dec;$l++)
{
if (ord($this->text[$storage_dec+$string_offset_dec+$l]) == '0') { continue; }
else { $font_tags[$name_id_dec] .= ($this->text[$storage_dec+$string_offset_dec+$l]); }
}
}
}
return $font_tags;
} // public function getFontInfo
/**
* function getCopyright()
* #public
* #return 'Copyright notice' contained in the TTF 'name' table at index 0
*/
public function getCopyright()
{
$this->info = $this->getFontInfo();
return $this->info[0];
} // public function getCopyright
/**
* function getFontFamily()
* #public
* #return 'Font Family name' contained in the TTF 'name' table at index 1
*/
public function getFontFamily()
{
$this->info = $this->getFontInfo();
return $this->info[1];
} // public function getFontFamily
/**
* function getFontSubFamily()
* #public
* #return 'Font Subfamily name' contained in the TTF 'name' table at index 2
*/
public function getFontSubFamily()
{
$this->info = $this->getFontInfo();
return $this->info[2];
} // public function getFontSubFamily
/**
* function getFontId()
* #public
* #return 'Unique font identifier' contained in the TTF 'name' table at index 3
*/
public function getFontId()
{
$this->info = $this->getFontInfo();
return $this->info[3];
} // public function getFontId
/**
* function getFullFontName()
* #public
* #return 'Full font name' contained in the TTF 'name' table at index 4
*/
public function getFullFontName()
{
$this->info = $this->getFontInfo();
return $this->info[4];
} // public function getFullFontName
/**
* function dec2ord()
* Used to lessen redundant calls to multiple functions.
* #protected
* #return object
*/
protected function dec2ord($dec)
{
return $this->dec2hex(ord($dec));
} // protected function dec2ord
/**
* function dec2hex()
* private function to perform Hexadecimal to decimal with proper padding.
* #protected
* #return object
*/
protected function dec2hex($dec)
{
return str_repeat('0', 2-strlen(($hex=strtoupper(dechex($dec))))) . $hex;
} // protected function dec2hex
/**
* function dec2hex()
* private function to perform Hexadecimal to decimal with proper padding.
* #protected
* #return object
*/
protected function exitClass($message)
{
echo $message;
exit;
} // protected function dec2hex
/**
* function dec2hex()
* private helper function to test in the file in question is a ttf.
* #protected
* #return object
*/
protected function is_ttf($file)
{
$ext = explode('.', $file);
$ext = $ext[count($ext)-1];
return preg_match("/ttf$/i",$ext) ? true : false;
} // protected function is_ttf
} // class ttfInfo
function getFontInfo($resource)
{
$ttfInfo = new ttfInfo;
$ttfInfo->setFontFile($resource);
return $ttfInfo->getFontInfo();
}
?>
Update 2021
Here is an updated version of the class with some fixes
https://github.com/HusamAamer/TTFInfo.git
Very similar to the previously posted answer... I've been using this class for a long time now.
class fontAttributes extends baseClass
{
// --- ATTRIBUTES ---
/**
* #access private
* #var string
*/
private $_fileName = NULL ; // Name of the truetype font file
/**
* #access private
* #var string
*/
private $_copyright = NULL ; // Copyright
/**
* #access private
* #var string
*/
private $_fontFamily = NULL ; // Font Family
/**
* #access private
* #var string
*/
private $_fontSubFamily = NULL ; // Font SubFamily
/**
* #access private
* #var string
*/
private $_fontIdentifier = NULL ; // Font Unique Identifier
/**
* #access private
* #var string
*/
private $_fontName = NULL ; // Font Name
/**
* #access private
* #var string
*/
private $_fontVersion = NULL ; // Font Version
/**
* #access private
* #var string
*/
private $_postscriptName = NULL ; // Postscript Name
/**
* #access private
* #var string
*/
private $_trademark = NULL ; // Trademark
// --- OPERATIONS ---
private function _returnValue($inString)
{
if (ord($inString) == 0) {
if (function_exists('mb_convert_encoding')) {
return mb_convert_encoding($inString,"UTF-8","UTF-16");
} else {
return str_replace(chr(00),'',$inString);
}
} else {
return $inString;
}
} // function _returnValue()
/**
* #access public
* #return integer
*/
public function getCopyright()
{
return $this->_returnValue($this->_copyright);
} // function getCopyright()
/**
* #access public
* #return integer
*/
public function getFontFamily()
{
return $this->_returnValue($this->_fontFamily);
} // function getFontFamily()
/**
* #access public
* #return integer
*/
public function getFontSubFamily()
{
return $this->_returnValue($this->_fontSubFamily);
} // function getFontSubFamily()
/**
* #access public
* #return integer
*/
public function getFontIdentifier()
{
return $this->_returnValue($this->_fontIdentifier);
} // function getFontIdentifier()
/**
* #access public
* #return integer
*/
public function getFontName()
{
return $this->_returnValue($this->_fontName);
} // function getFontName()
/**
* #access public
* #return integer
*/
public function getFontVersion()
{
return $this->_returnValue($this->_fontVersion);
} // function getFontVersion()
/**
* #access public
* #return integer
*/
public function getPostscriptName()
{
return $this->_returnValue($this->_postscriptName);
} // function getPostscriptName()
/**
* #access public
* #return integer
*/
public function getTrademark()
{
return $this->_returnValue($this->_trademark);
} // function getTrademark()
/**
* Convert a big-endian word or longword value to an integer
*
* #access private
* #return integer
*/
private function _UConvert($bytesValue,$byteCount)
{
$retVal = 0;
$bytesLength = strlen($bytesValue);
for ($i=0; $i < $bytesLength; $i++) {
$tmpVal = ord($bytesValue{$i});
$t = pow(256,($byteCount-$i-1));
$retVal += $tmpVal*$t;
}
return $retVal;
} // function UConvert()
/**
* Convert a big-endian word value to an integer
*
* #access private
* #return integer
*/
private function _USHORT($stringValue) {
return $this->_UConvert($stringValue,2);
}
/**
* Convert a big-endian word value to an integer
*
* #access private
* #return integer
*/
private function _ULONG($stringValue) {
return $this->_UConvert($stringValue,4);
}
/**
* Read the Font Attributes
*
* #access private
* #return integer
*/
private function readFontAttributes() {
$fontHandle = fopen($this->_fileName, "rb");
// Read the file header
$TT_OFFSET_TABLE = fread($fontHandle, 12);
$uMajorVersion = $this->_USHORT(substr($TT_OFFSET_TABLE,0,2));
$uMinorVersion = $this->_USHORT(substr($TT_OFFSET_TABLE,2,2));
$uNumOfTables = $this->_USHORT(substr($TT_OFFSET_TABLE,4,2));
// $uSearchRange = $this->_USHORT(substr($TT_OFFSET_TABLE,6,2));
// $uEntrySelector = $this->_USHORT(substr($TT_OFFSET_TABLE,8,2));
// $uRangeShift = $this->_USHORT(substr($TT_OFFSET_TABLE,10,2));
// Check is this is a true type font and the version is 1.0
if ($uMajorVersion != 1 || $uMinorVersion != 0) {
fclose($fontHandle);
throw new Exception($this->_fileName.' is not a Truetype font file') ;
}
// Look for details of the name table
$nameTableFound = false;
for ($t=0; $t < $uNumOfTables; $t++) {
$TT_TABLE_DIRECTORY = fread($fontHandle, 16);
$szTag = substr($TT_TABLE_DIRECTORY,0,4);
if (strtolower($szTag) == 'name') {
// $uCheckSum = $this->_ULONG(substr($TT_TABLE_DIRECTORY,4,4));
$uOffset = $this->_ULONG(substr($TT_TABLE_DIRECTORY,8,4));
// $uLength = $this->_ULONG(substr($TT_TABLE_DIRECTORY,12,4));
$nameTableFound = true;
break;
}
}
if (!$nameTableFound) {
fclose($fontHandle);
throw new Exception('Can\'t find name table in '.$this->_fileName) ;
}
// Set offset to the start of the name table
fseek($fontHandle,$uOffset,SEEK_SET);
$TT_NAME_TABLE_HEADER = fread($fontHandle, 6);
// $uFSelector = $this->_USHORT(substr($TT_NAME_TABLE_HEADER,0,2));
$uNRCount = $this->_USHORT(substr($TT_NAME_TABLE_HEADER,2,2));
$uStorageOffset = $this->_USHORT(substr($TT_NAME_TABLE_HEADER,4,2));
$attributeCount = 0;
for ($a=0; $a < $uNRCount; $a++) {
$TT_NAME_RECORD = fread($fontHandle, 12);
$uNameID = $this->_USHORT(substr($TT_NAME_RECORD,6,2));
if ($uNameID <= 7) {
// $uPlatformID = $this->_USHORT(substr($TT_NAME_RECORD,0,2));
$uEncodingID = $this->_USHORT(substr($TT_NAME_RECORD,2,2));
// $uLanguageID = $this->_USHORT(substr($TT_NAME_RECORD,4,2));
$uStringLength = $this->_USHORT(substr($TT_NAME_RECORD,8,2));
$uStringOffset = $this->_USHORT(substr($TT_NAME_RECORD,10,2));
if ($uStringLength > 0) {
$nPos = ftell($fontHandle);
fseek($fontHandle,$uOffset + $uStringOffset + $uStorageOffset,SEEK_SET);
$testValue = fread($fontHandle, $uStringLength);
if (trim($testValue) > '') {
switch ($uNameID) {
case 0 : if ($this->_copyright == NULL) {
$this->_copyright = $testValue;
$attributeCount++;
}
break;
case 1 : if ($this->_fontFamily == NULL) {
$this->_fontFamily = $testValue;
$attributeCount++;
}
break;
case 2 : if ($this->_fontSubFamily == NULL) {
$this->_fontSubFamily = $testValue;
$attributeCount++;
}
break;
case 3 : if ($this->_fontIdentifier == NULL) {
$this->_fontIdentifier = $testValue;
$attributeCount++;
}
break;
case 4 : if ($this->_fontName == NULL) {
$this->_fontName = $testValue;
$attributeCount++;
}
break;
case 5 : if ($this->_fontVersion == NULL) {
$this->_fontVersion = $testValue;
$attributeCount++;
}
break;
case 6 : if ($this->_postscriptName == NULL) {
$this->_postscriptName = $testValue;
$attributeCount++;
}
break;
case 7 : if ($this->_trademark == NULL) {
$this->_trademark = $testValue;
$attributeCount++;
}
break;
}
}
fseek($fontHandle,$nPos,SEEK_SET);
}
}
if ($attributeCount > 7) {
break;
}
}
fclose($fontHandle);
return true;
}
/**
* #access constructor
* #return void
*/
function __construct($fileName='') {
if ($fileName == '') {
throw new Exception('Font File has not been specified') ;
}
$this->_fileName = $fileName;
if (!file_exists($this->_fileName)) {
throw new Exception($this->_fileName.' does not exist') ;
} elseif (!is_readable($this->_fileName)) {
throw new Exception($this->_fileName.' is not a readable file') ;
}
return $this->readFontAttributes();
} // function constructor()
} /* end of class fontAttributes */
Why reinvent the wheel when the fine people at DOMPDF project has already done the work for you? Take a look at php-font-lib # https://github.com/PhenX/php-font-lib. This has all the features that you have asked for and supports other font formats as well. Look at the demo UI # http://pxd.me/php-font-lib/www/font_explorer.html to get an idea about what kind of information you can get from this library.