четверг, февраля 10, 2011

Как на самом деле работает присваивание и передача по ссылке объектов в php5

Как-то давно, когда только появился php5, я думал, что разобрался с ООП, а именно с присвоением объектов. Но из-за того, что много времени проводил с java стал под забывать как это работает в php5.
В штатном режиме, если всегда помнить о том, что в php5 присваивание происходит по ссылке, то нет никаких проблем, пока не используешь знак &.
http://www.php.net/manual/en/language.oop5.basic.php
<?php
class SimpleClass{}
class SimpleClass
{
    // property declaration
    public $var = 'a default value';

    // method declaration
    public function displayVar() {
        echo $this->var;
    }
}
?>

Но подлинный смысл раскрывается этим комментарием:
http://www.php.net/manual/en/language.oop5.basic.php#79856
Я позволил себе его вольно перевести:
В php необходимо думать о переменных как о ячейках памяти. У каждой переменной если имя, которое ссылается на ячейку памяти(переменную), где хранится значение простого типа: число, строка, массив, и т.д. Когда вы создаете ссылку(&), вы создаете второе имя, которое ссылается на ту же саму ячейку памяти. Когда вы присваиваете одну переменную другой, вы копируете содержимое ячейки памяти в другую ячейку памяти.
Но присваивание экземпляров классов(далее объект) происходит не так как присваивание простых типов. Объекты не хранятся в ячейках памяти, которые программист "видит" на прямую. Вместо этого в ячейке памяти хранится указатель на объект. Таким образом указатель ведет себя как примитивный тип.
Когда вы присваиваете значение ссылки объекта одной переменной другой, обе переменные могут менять состояние одного и того же объекта. Но переменные не являются ссылками на объект, т.к. если присвоить одной из переменных новое значение, то это не отразится на другой переменной.
<?php
// Assignment of an object
Class Object{
   public $foo="bar";
};

$objectVar = new Object();
$reference =& $objectVar;
$assignment = $objectVar

//
// $objectVar --->+-----------+
//                |(указатель1)----+
// $reference --->+-----------+    |
//                                 |
//                +-----------+    |
// $assignment -->|(указатель2)----+
//                +-----------+    |
//                                 |
//                                 v
//                  Object(1):foo="bar"
//
?>
Значение переменной $assignment отличается от $objectVar, но эти значение ссылаются на один и тот же объект. Это поведение делает похожим на механизм передачи по ссылке. Если вы используете переменную $objectVar, чтобы поменять состояние объекта, то эти же изменения появятся и в переменной $assignment, т.к. они указывают на один и тот же объект.


<?php
$objectVar->foo = "qux";
print_r( $objectVar );
print_r( $reference );
print_r( $assignment );

//
// $objectVar --->+-----------+
//                |(указатель1)----+
// $reference --->+-----------+    |
//                                 |
//                +-----------+    |
// $assignment -->|(указатель2)----+
//                +-----------+    |
//                                 |
//                                 v
//                  Object(1):foo="qux"
//
?>

Но с передачей значения по ссылке (&) дело обстоит совсем иначе. Если вы обнулите $objectVar, вы замените значение указателя в ячейке памяти на NULL. Это означает, что $reference, которая ссылается на туже ячейку памяти, также будет NULL. Но $assignment, которая ссылается на другую ячейку памяти, будет так же хранить копию указателя на объект, и она не будет равна NULL.

<?php
$objectVar = null;
print_r($objectVar);
print_r($reference);
print_r($assignment);

//
// $objectVar --->+-----------+
//                |  NULL     | 
// $reference --->+-----------+
//                           
//                +-----------+
// $assignment -->|(указатель2)----+
//                +-----------+    |
//                                 |
//                                 v
//                  Object(1):foo="qux"
?>


К примеру, если взять java, где все переменные если ссылки на объекты, тот же пример выполняется так как и в php5 без символа &:

import java.util.*;

class ObjectReference {
    public static void main(String[] args) {
        class Obj {
            int var = 1;
        }
        
        Obj objectVar = new Obj();
        Obj reference = objectVar;
        objectVar.var = 2;
        System.out.println("objectVar.var="+objectVar.var);
        System.out.println("reference.var="+reference.var);
        
        objectVar = null;
        
        if (reference == null) {
            System.out.println("reference is null");
        } else {
            System.out.println("reference is not null");
        }
    }
}
ответ:
objectVar.var=2
reference.var=2
reference is not null

четверг, января 20, 2011

Программа для сбора логов в клиентов

Очень полезно, когда у клиента что то не работает и ты не знаешь чем помочь
http://code.google.com/p/android-log-collector/

Как сбросить logcat в файл


try {
File filename = new File(Environment.getExternalStorageDirectory()+"/logfile.log");
filename.createNewFile();
String cmd = "logcat -d -f "+filename.getAbsolutePath();
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

пятница, декабря 24, 2010

А как умеет ваш язык это делать?

Результат будет одинаковый:
about_logo

python -c 'import re;s="http://www.google.ru/intl/ru/images/about_logo.gif"; m = re.match(".*\/(.*)\.gif$",s); print m.group(1)'
perl -e '$e="http://www.google.ru/intl/ru/images/about_logo.gif"; $e =~ /.*\/(.*)\.gif$/i; print $1."\n";'
php -r '$e="http://www.google.ru/intl/ru/images/about_logo.gif"; $pat = "/.*\/(.*)\.gif$/"; preg_match($pat,$e,$m);print $m[1]."\n";'

вторник, декабря 07, 2010

Android SDK 2.3 - "Unknown Android Packaging Problem"

После обновления SDK согласно документам
http://developer.android.com/intl/zh-TW/sdk/adding-components.html
http://developer.android.com/intl/zh-TW/sdk/installing.html
http://developer.android.com/intl/zh-TW/sdk/eclipse-adt.html

перестала работать отладка adb
Эта программа переехала в другую папку, нужно обновить .bashrc
export PATH=${PATH}:/tools:/platform-tools
т.е. по сути добавить папку platform-tools
Далее перестала работать сборка, выходит следующая ошибка
Error generating final archive: java.io.FileNotFoundException: 
/home/user/android/bin/resources.ap_ does not exist
Android
Unknown Android Packaging Problem

Решение нашел здесь
http://stackoverflow.com/questions/4372574/many-errors-in-android-project-after-i-upgraded-to-android-sdk-2-3
Вкратце, если в файле res/values/strings.xml вы используете конструкции вида
<string name="page_number">%d results, showing page %d of %d</string> 
то теперь нужно использовать индексы для элементов больше одного, т.е. вот так
<string name="page_number">%1$d results, showing page %2$d of %3$d</string> 

четверг, ноября 11, 2010

Глобальная переменная $_REQUEST в php

Заметил интересное поведение в глобальной переменной
$_REQUEST в php, похоже она создается по запросу
~$ php -r 'echo (int)isset($GLOBALS["_REQUEST"])."\n";'

ответ 0, но если
~$ php -r '$_REQUEST;echo (int)isset($GLOBALS["_REQUEST"])."\n";'

ответ уже 1

понедельник, ноября 08, 2010

понедельник, августа 30, 2010

Mysql ERROR 1005 (HY000): Can't create table (errno: 150)

Уже который раз спотыкаюсь об одни и те же грабли, решил записать.

т.е. после создания таблиц:

CREATE TABLE sf_guard_user
(
id BIGINT AUTO_INCREMENT,
first_name VARCHAR(255),
last_name VARCHAR(255),
email_address VARCHAR(255) NOT NULL UNIQUE,
username VARCHAR(128) NOT NULL UNIQUE,
algorithm VARCHAR(128) DEFAULT 'sha1' NOT NULL,
salt VARCHAR(128), password VARCHAR(128),
is_active TINYINT(1) DEFAULT '1',
is_super_admin TINYINT(1) DEFAULT '0',
last_login DATETIME, created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
INDEX is_active_idx_idx (is_active),
PRIMARY KEY(id)
)ENGINE = INNODB;

CREATE TABLE sf_guard_user_profile
(
id BIGINT AUTO_INCREMENT,
user_id BIGINT, first_name VARCHAR(20),
last_name VARCHAR(20),
email VARCHAR(255),
email_hash VARCHAR(255),
INDEX user_id_idx (user_id),
PRIMARY KEY(id)
) ENGINE = INNODB;


я пытаюсь создать ключи:


ALTER TABLE sf_guard_user_profile
ADD CONSTRAINT sf_guard_user_profile_user_id_sf_guard_user_id
FOREIGN KEY (user_id)
REFERENCES sf_guard_user(id)
ON DELETE CASCADE;

и получаю ошибку ERROR 1005 (HY000): Can't create table (errno: 150)
mysql говорит, что не может найти поле на которое ссылаемся

mysql> SHOW ENGINE INNODB STATUS;
...
100830 20:59:41 Error in foreign key constraint of table s1/#sql-477_152:
FOREIGN KEY (user_id) REFERENCES sf_guard_user(id) ON DELETE CASCADE:
Cannot find an index in the referenced table where the
referenced columns appear as the first columns, or column types
in the table and the referenced table do not match for constraint.
Note that the internal storage type of ENUM and SET changed in
tables created with >= InnoDB-4.1.12, and such columns in old tables
cannot be referenced by such columns in new tables.
See http://dev.mysql.com/doc/refman/5.1/en/innodb-foreign-key-constraints.html
for correct foreign key definition.
...


mysql это не нравится, отсюда и ошибка, хотя пишет что не может найти поле, а оно есть.
Дело оказывается в том, что в symfony при генерации схемы нет проверки на размерность типов для внешних ключей, т.е. если указать разные размерности для ключевых полей, то будет ошибка, т.е. вместо:

sfGuardUserProfile:
tableName: sf_guard_user_profile
columns:
user_id: integer(4)
first_name: varchar(20)
last_name: varchar(20)
email: varchar(255)
email_hash: varchar(255)
relations:
Users:
class: sfGuardUser
refClass: sfGuardUserGroup
local: group_id
foreign: user_id
foreignAlias: Groups
sfGuardUser:
type: one
foreignType: one
class: sfGuardUser
local: user_id
foreign: id
onDelete: cascade
foreignAlias: Profile

надо :

sfGuardUserProfile:
tableName: sf_guard_user_profile
columns:
user_id: integer
first_name: varchar(20)
last_name: varchar(20)
email: varchar(255)
email_hash: varchar(255)
relations:
Users:
class: sfGuardUser
refClass: sfGuardUserGroup
local: group_id
foreign: user_id
foreignAlias: Groups
sfGuardUser:
type: one
foreignType: one
class: sfGuardUser
local: user_id
foreign: id
onDelete: cascade
foreignAlias: Profile


а именно поле user_id должно быть таким же как в у sfGuardUser

среда, июня 02, 2010

Как сделать слайд-шоу для рабочего стола gnome

В ubuntu 9.10 появились обой рабочего стола в виде слайд-шоу



его пример лежит в папке
/usr/share/backgrounds/cosmos/background-1.xml

Это простой xml файл с простыми инструкциями.
Его можно создать вручную, схема довольно проста.
Но можно использовать этот скрипт:

#!/usr/bin/perl

$staticDuration = "300.0";#1795.0
$transitionDuration = "5.0";#5.0

print <<"TBEGIN";
<background>
<starttime>
<year>2009</year>
<month>08</month>
<day>04</day>
<hour>00</hour>
<minute>00</minute>
<second>00</second>
</starttime>
TBEGIN

$pwd = `pwd`;
$pwd =~ s/[\n]//;
$isStart = 0;
opendir(DIR,".");
while ($name = readdir(DIR)) {
if($name =~ /jpg|png/){
if($isStart){
$pathLast = $pwd."/".$lastName;
$pathCurr = $pwd."/".$name;
print <<"Tname";
<static>
<duration>1795.0</duration>
<file>$pathLast</file>
</static>
<transition>
<duration>5.0</duration>
<from>$pathLast</from>
<to>$pathCurr</to>
</transition>
Tname
} else {
$pathFirst = $pwd."/".$name;
}
$lastName = $name;
$isStart = 1;
}
}
closedir(DIR);

print <<"TEND"
<static>
<duration>1795.0</duration>
<file>$pathCurr</file>
</static>
<transition>
<duration>5.0</duration>
<from>$pathCurr</from>
<to>$pathFirst</to>
</transition>
</background>
TEND


создайте текстовый файл gen.pl с этим кодом и положите в папку с фотографиями.
выполните команду:
perl gen.pl > background-2.xml


Теперь добавьте этот файл в "Параметры внешнего вида" - "Фон", вуаля.

вторник, мая 18, 2010

Плагин по regexp для gedit

Потребовался инструмент для мелкого редактирования текста с использованием regexp
То что может стандартный gedit уже не хватает, такие вещи как замена теста с использованием спец-символов таких как отступ(\t), конец строки(\n) решаются довольно просто, но дальше уже сложно.
Есть такой плагин в одном блоге описано как его поставить
Я повторюсь, его нужно скачать и распаковать в папку ~/.gnome2/gedit/plugins/
Создать если не существует.
Теперь можно использовать такие конструкции как поиск функции название которой вы точно не помните, например ищете Что_то_тамSave
function\s+.*Save.*

вторник, апреля 20, 2010

Настройка времени под linux

Самый простой способ:
sudo ntpdate 0.ru.pool.ntp.org

Список всех серверов:
0.ru.pool.ntp.org
1.ru.pool.ntp.org
2.ru.pool.ntp.org
3.ru.pool.ntp.org

Теперь по порядку:

1.Установить часовой пояс


sudo dpkg-reconfigure tzdata

2.Синхронизация


ntpdate 0.ru.pool.ntp.org

3.Настройка обновления


$ sudo bash
# echo "ntpdate 0.ru.pool.ntp.org" > /etc/cron.daily/ntpdate
# chmod 755 /etc/cron.daily/ntpdate

четверг, апреля 08, 2010

Простой способ вывода сообщения


public class MyActivity extends Activity
{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toast.makeText(
MyActivity.this,
"Ваше сообщение",
Toast.LENGTH_LONG
).show();
}
}

вторник, апреля 06, 2010

Как передать картинку в Activity через Intent

Короткий пример:

Uri imageUri = intent.getData();
mBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), imageUri);
Intent intent = new Intent(TakePictureActivity.this, PreviewActivity.class);
intent.putExtra(EXTRA_BITMAP_DATA, mBitmap);
startActivityForResult(intent, REQUEST_PREVIEW);

среда, июля 15, 2009

Полезные заметки по symfony - 2

Если надо использовать функции из View в Controller, то надо вопользоваться sfLoader

class blogActions extends sfActions
{
public function preExecute(){
...
}
public function executeRedirect(sfWebRequest $request)
{
sfLoader::loadHelpers('Url');
$this->redirect(url_for('@archive_blog?id='.$this->currentBlog->getId(), 301);
}
}


Но лучше использовать внутренние средства, в symfony почти для каждого из составляющих MVC если дублирующие средства:

class blogActions extends sfActions
{
public function preExecute(){
...
}
public function executeRedirect(sfWebRequest $request)
{
sfLoader::loadHelpers('Url');
$this->redirect(
$this->getController()->genUrl('@archive_blog?id='.$this->currentBlog->getId()),
301
);
}
}


Исключение составляет вызов action из View, как например в ZendFramework, но в symfony похоже это не трубуется (прим. автора: по крайней мере в моей практике).

среда, июля 08, 2009

Как добавить в форму admin генератора ссылку

К примеру у вас есть backend (admin) и вам нужно поставить ссылку на какой нибудь ресурс.
Для этого нужно создать свой виджет, например, такой:



class sfWidgetFormHtmlLink extends sfWidgetForm
{

protected function configure($options = array(), $attributes = array())
{
$this->addOption('url', false);
$this->addOption('target', 'self'); // self, blank
$this->addOption('route', false);
$this->addOption('name', true);
}

public function render($name, $value = null, $attributes = array(), $errors = array())
{
if($this->getOption('route')){
return link_to(
$this->getOption('name'),
$this->getOption('route'),
array('target'=>'_'.$this->getOption('target'))
);
}

if($this->getOption('url')){
return "<a href='".$this->getOption('name').
"' target='_".$this->getOption('target')."'>".
$this->getOption('name')."</a>";
}

}
}


Теперь его просто использовать:

class SomeForm extends BaseSomeForm
{

public function configure()
{
$this->setWidget('external_link',new sfWidgetFormHtmlLink(array('url'=>'http://ya.ru','name'=>'Поискать')));
}
}


Меняя метод render можно создать свой виджет по выводу какого-либо html текста и т.д.

среда, июня 17, 2009

Полезные заметки по symfony - 1

Установка meta html данных:

class SomeObjectActions extends sfActions
{
public function executeShowObject(sfWebRequest $request){
...
$this->getResponse()->setTitle($this->object->getMetaTitle());
$this->getResponse()->addMeta('meta_keywords', $this->object->getMetaKeywords());
$this->getResponse()->addMeta('meta_description', $this->object->getMetaDescription());
}
}

Правильное использование sfWidgetFormTextareaTinyMCE

sfWidgetFormTextareaTinyMCE - это виджет, HTML WYSIWYG editor. Очень удобная вешь, но в блоге symfony очень скудно описан принцип работы, точнее в последней версии возникает ошибка:

You must pass a "class" attribute for a TinyMCE widget

Вот, как и какой класс надо указывать:

$this->setWidget('text', new sfWidgetFormTextareaTinyMCE(array(
'width' => 650,
'height' => 550,
'config' => 'theme_advanced_disable: "anchor,image,cleanup,help"',
)
,array(
'class' => 'tinyMCE',
)
));


в генераторе (если вы используете его в backend) generator.yml укажите class в атрибутах

config:
actions: ~
fields:
text: { label: 'Текст', attributes: { class: "tinyMCE" } }


еще обсуждение здесь

вторник, июня 16, 2009

Как заставить mysql5 использовать нужный вам default-character и collation

Для начала посмотрите что у вас есть


SHOW VARIABLES LIKE 'character_set%';
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.00 sec)


По умолчанию Value=latin1

Теперь вы хотите чтобы все клиенты mysql сразу использовали нужную ва кодировку:
utf8,cp1251 или koi8r

Нужно добавить в файл my.cnf
/etc/mysql/my.cnf

Слудущие переменные:

[client]
default-character-set=utf8
[mysqld]
default-character-set=utf8
default-collation=utf8_general_ci
character-set-server=utf8
init-connect='SET NAMES utf8;'
collation-server=utf8_general_ci
[mysql]
default-character-set=utf8


После изменений перезагружайте сервер.
/etc/init.d/mysql restart

Если не работает еще раз проверьте что у вас происходит:

SHOW VARIABLES LIKE 'character_set%';


проверьте кодировку базы данных:

mysql> show create database yourdatabase;
+-------------+----------------------------------------------------------------------+
| Database | Create Database |
+-------------+----------------------------------------------------------------------+
| yourdatabase | CREATE DATABASE `yourdatabase` /*!40100 DEFAULT CHARACTER SET utf8 */ |
+-------------+----------------------------------------------------------------------+
1 row in set (0.00 sec)


Кодировка по умолчанию для создания таблиц наследуется.

Можно еще проще для ubuntu (10.04):
создать файл, например /etc/mysql/conf.d/mysqld_charset.cnf
с текстом:

[mysqld]
default-character-set=utf8
default-collation=utf8_general_ci
character-set-server=utf8
init-connect='SET NAMES utf8;'
collation-server=utf8_general_ci

[mysql]
default-character-set=utf8


и перезапустить mysql

пятница, мая 08, 2009

Полезные команды для web-разработчика

Искать любое упоминание чего-либо в проекте, исключая системные svn файлы:
grep -Rn 'mysql.php' ./ | grep -v svn

Перенести изменения из одного проекта в другой методом патча:
project1 $ svn diff > filename.patch
project2 $ patch -p0 -i ../project1/filename.patch

Собрать файлы в архив по определенному принципу, в данном случае измененные и добавленные файлы:
perl -e '$res = `svn stat|grep -E "^[AM]"`; $res =~ s/\+|^\w+\s*//gm; $res =~ s/\n/ /g; `tar cfz 1.tgz $res`;'


В буду дальнейшем буду пополнять, есть что то вспомню.